Notify of change to list

Alan J. Salmoni salmoni at gmail.com
Mon Jun 16 07:17:40 EDT 2008


Paul - thank you very much for your help, it was much appreciated. I
thought that this was the kind of thing I had to do.

I also messed around with properties to try and solve this problem,
but noticed a similar thing (ie, that changing a mutable attribute's
element is different to rebinding it): when a "propertied" attribute
is rebound, the set method is called as expected. However, when
setting an element of a mutable like a list, the get method is called
and the set is not. This seems to be a bit of a gotcha. I'm sure that
calling the get method is justified in the terms of Python internals,
but as a user it seems a little counter-intuitive. Without being an
expert on language design, my first thought was that changing an
element was a "set" operation. Here's a toy:

class tobj(object):
    def __init__(self):
        self._x = [0,1,2,3,4,5,6]

    def setx(self, x):
        print "At setx"
        self._x = x

    def getx(self):
        print "At getx"
        return self._x

    d = property(getx, setx)

a = tobj()
print "setting one element only"
a.d[1] = 3
print "rebinding the attribute"
a.d = 99

which comes up with:

setting one element only
At getx
rebinding the attribute
At setx

Does anyone know why it works like this? I would guess that to "set"
one element, the attribute needs to be retrieved which means the get
method is called. I also guess that only rebinding calls the set
method which is why it isn't called here.

Alan

On Jun 13, 11:19 am, Paul Hankin <paul.han... at gmail.com> wrote:
> On Jun 13, 12:00 pm, "Alan J.Salmoni" <salm... at gmail.com> wrote:
>
> > My question is how can my program be notified of a change to a class
> > attribute that is a list?
>
> You can't. But you can replace lists inside your class with a list
> that notifies changes.
>
> Something like this:
>
> class NotifyingList(list):
>     def __setitem__(self, i, v):
>         print 'setting', i, 'to', v
>         list.__setitem__(self, i, v)
>
> [[You'll likely need to redefine __setslice__, __delitem__,
> __delslice__, append and extend as well]].
>
> >>> x = NotifyingList([1, 2, 3])
> >>> x[1] = 4
> setting 1 to 4
> >>> x
>
> [1, 4, 3]
>
> If users of your class are allowed to replace attributes with their
> own lists, you'll have to catch these and convert to NotifyingLists;
> and it may be somewhat messy.
>
> I hope this is useful to you.
>
> --
> Paul Hankin




More information about the Python-list mailing list