views:

197

answers:

2

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.

+1  A: 

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
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 Chelliah
@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
+1  A: 

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])
Shailesh Kumar