Can someone help me with this bug?

Duncan Smith buzzard at urubu.freeserve.co.uk
Thu May 27 19:02:41 EDT 2004


"Peter Otten" <__peter__ at web.de> wrote in message
news:c95gsa$492$04$1 at news.t-online.com...
> Duncan Smith wrote:
>
> >          I'm probably missing something that should be obvious, but can
> > anyone tell me what's wrong with the following?
> >
> > temp.py
> > ---------------------------------------------------------------------
> >
> > class Item(object):
> >     def __init__(self, id, data):
> >         self.id = id
> >         self.data = data
> >
> >
> > class KeyedSet(dict):
> >     def __init__(self, items=None):
> >         if items is not None:
> >             for item in items:
> >                 self[item.id] = item
> >
> >     def __iter__(self):
> >         return self.itervalues()
> >
> >     def __repr__(self):
> >         return '%s(%r)' % (self.__class__.__name__, self.keys())
> >
> >     def __contains__(self, item):
> >         return self.has_key(item.id)
> >
> >     def intersection(self, other):
> >         res = self.__class__()
> >         for item in self:
> >             if item in other:
> >                 res[item.id] = item
> >         return res
> >
> >     def __and__(self, other):
> >         return self.intersection(other)
> >
> >     def intersection_update(self, other):
> >         self &= other
> >
> >     def __iand__(self, other):
> >         self = self.intersection(other)
> >         return self
> > --------------------------------------------------------------------
> >
> >>>> from temp import Item, KeyedSet
> >>>> a = Item(0, 'W')
> >>>> b = Item(1, 'X')
> >>>> c = Item(2, 'Y')
> >>>> d = Item(3, 'Z')
> >>>> aset = KeyedSet([a, b, c])
> >>>> bset = KeyedSet([b, c, d])
> >>>> aset &= bset
> >>>> aset
> > KeyedSet([1, 2])
> >>>> aset = KeyedSet([a, b, c])
> >>>> aset.intersection_update(bset)
> >>>> aset
> > KeyedSet([0, 1, 2])
> >>>>
> >
> > I can't figure out why 'aset' is unchanged?  Thanks.
>
> Your problem stripped down to the bare bones: __iand__() creates a new
> instance instead of modifying the current one.
>
> self = something
>
> doesn't copy something's data to self, it just rebinds self to something
for
> the rest of the method.
>
> A minimal example of what went wrong:
>
> >>> class A:
> ...     def __init__(self, value):
> ...             self.value = value
> ...     def __iand__(self, other):
> ...             return A(self.value + other.value)
> ...     def __repr__(self):
> ...             return "value=%s" % self.value
> ...
> >>> a = b = A(1)
> >>> a &= A(2)
>
> It looks like inplace modification, but isn't:
>
> >>> a, b
> (value=3, value=1)
>
> a is rebound to the newly created instance, which tricks you into
believing
> it was modified. The backup reference b reveals the error.
> Here's the correction (I'm lazy, so I reuse the unaltered parts of A):
>
> >>> class B(A):
> ...     def __iand__(self, other):
> ...             self.value += other.value
> ...             return self
> ...
> >>> a = b = B(1)
> >>> a &= B(2)
> >>> a, b
> (value=3, value=3)
>
> Once you understand the basic principle, it should be no problem to apply
it
> to your KeyedSet class. If in doubt, use the sets.Set implementation as a
> template.
>
> Peter
>

Yep.  This also explains why the other unit tests involving __iand__ passed,
leaving me thinking it was OK.  Cheers.

Duncan






More information about the Python-list mailing list