3

ZODB provides a PersistentList and a PersistentMapping, but I'd like a PersistentSet. I wrote a quick class that mirrors the ancient PersistentList from ZODB 2. Because there's no UserSet in Python, I had to extend from the C-based built-in set.

class PersistentSet(UserSet, Persistent):
    def __iand__(self, other):
        set.__iand__(other)
        self._p_changed = 1

    ...

    ...

    ...

    def symmetric_difference_update(self, other):
        set.symmetric_difference_update(other)
        self._p_changed = 1

The code produced a "multiple bases have instance lay-out conflict" error. I tried creating a UserSet wrapper around set, but that didn't solve the problem either.

class UserSet(set):
    def __init__(self):
        self.value = set
    def __getattribute__(self, name):
        return self.value.__getattribute__(name

Finally, I imported sets.Set (superseded by the built-in set), but that seems to be implemented in C, too. I didn't find any set implementations on PyPI so I'm at a dead end now.

What are my options? I may have to implement a set from scratch or use UserDict and throw away all the values.

Community
  • 1
  • 1
Nikhil
  • 5,705
  • 1
  • 32
  • 30

3 Answers3

3

Why don't you use the persistent set class provided with the BTree libraries in ZODB. There are 4 such classes available. IITreeSet and IOTreeSet manage sets of integers and OITreeSet and OOTreeSet manage set of arbitrary objects. They correspond to the four BTree classes IIBTree, IOBTree, OIBTree and OOBTree respectively. Their advantages over the set implementation built into Python are their fast lookup mechanism (thanx to the underlying BTree) and their persistence support.

Here is some sample code:

>>> from BTrees.IIBTree import IITreeSet, union, intersection
>>> a = IITreeSet([1,2,3])
>>> a
<BTrees._IIBTree.IITreeSet object at 0x00B3FF18>
>>> b = IITreeSet([4,3,2])
>>> list(a)
[1, 2, 3]
>>> list(b)
[2, 3, 4]
>>> union(a,b)
IISet([1, 2, 3, 4])
>>> intersection(a,b)
IISet([2, 3])
raben
  • 3,060
  • 5
  • 32
  • 34
Shailesh Kumar
  • 6,457
  • 8
  • 35
  • 60
1

For future readings, I just wanted to offer a slight improvement over the already proposed answers...

Custom persistent set class

class PersistentSet(Persistent):

    def __init__(self, *args, **kwargs):
        self._set = set(*args, **kwargs)

    def __getattr__(self, name):
        return getattr(self._set, name)

Persistent set class from the library

from BTrees.OOBTree import OOSet

See also

Community
  • 1
  • 1
Mathieu Rodic
  • 6,637
  • 2
  • 43
  • 49
1

Forward all attribute requests to the internal set:

class PersistentSet(Persistent):
    def __init__(self):
        self.inner_set = set()

    def __getattribute__(self, name):
        try:
            inner_set = Persistent.__getattribute__(self, "inner_set")
            output = getattr(inner_set, name)
        except AttributeError:
            output = Persistent.__getattribute__(self, name)

        return output
Unknown
  • 45,913
  • 27
  • 138
  • 182
  • Your solution works, but I made a couple changes. I added *args to the constructor, and I added self._p_changed=1 at the end of the try block...I'm not sure if that's unnecessary or if it was accidentally omitted in the response. Thanks. – Nikhil May 18 '09 at 21:23
  • @Nikhil, it is an easy shortcut. You will just assume that every variable access changes the data. If you want it to be perfect, just wrap every method that changes the set with self._p_changed = 1 – Unknown May 18 '09 at 21:48