detecting property modification

0 views
Skip to first unread message

Mangabasi

unread,
Dec 21, 2007, 12:26:36 PM12/21/07
to
Howdy,

I think it is easier to explain my question with a short example:

class Body:
def __init__(self, pos):
self.__dict__['pos'] = pos

def __setattr__(self, name, value):
if name == 'pos':
print 'pos changed to', value

self.__dict__[name] = value

>>> b = Body([0, 0])
>>> b.pos = [3, 4]
>>> pos changed to [3, 4]
>>> b.pos[0] = 5 # How do I detect this in the Body?
>>>


I would like to print 'pos changed to [5, 4]' when pos is modified
with direct access to the pos object (i.e. b.pos[0] = 5)


My first instinct is to modify the Body's __dict__ by using
metaclasses.
I don't have any experience with meta classes but from what I remember
this should be possible but I thought there could be a simpler way to
do this.

What is the Pythonic way to do this? Can anybody provide a short
example for either case (with or without metaclasses).

Regards


Jean-Paul Calderone

unread,
Dec 21, 2007, 12:40:56 PM12/21/07
to pytho...@python.org

You don't need metaclasses. You do need a wrapper around the list
so that you can intercept calls to __setitem__ (and whatever else)
which are made onto it.

You might also consider making Body new-style (subclass object) and
using a property instead of a __setattr__ implementation. You still
need a wrapper, but the implementation of the rest of the feature
should be simpler.

Jean-Paul

Mangabasi

unread,
Dec 21, 2007, 12:49:51 PM12/21/07
to
On Dec 21, 12:40 pm, Jean-Paul Calderone <exar...@divmod.com> wrote:
> Jean-Paul- Hide quoted text -
>
> - Show quoted text -

Hi Jean-Paul,

Sorry, I should have spelled this out in my post but I did not. For
several reasons I do not wish to couple the pos object with the Body
instances. In my case, I did not want pos objects (in my example they
were lists but they can be other objects as well) to care about their
parents. So the question is: How do we detect this in the Body?

Jean-Paul Calderone

unread,
Dec 21, 2007, 1:11:55 PM12/21/07
to pytho...@python.org
On Fri, 21 Dec 2007 09:49:51 -0800 (PST), Mangabasi <mang...@gmail.com> wrote:
> [snip]

>
>Hi Jean-Paul,
>
>Sorry, I should have spelled this out in my post but I did not. For
>several reasons I do not wish to couple the pos object with the Body
>instances. In my case, I did not want pos objects (in my example they
>were lists but they can be other objects as well) to care about their
>parents. So the question is: How do we detect this in the Body?
>

You can't. If you have this:

b = Body([1, 2])
b.pos[0] = 3


Then Body gets asked for the value of the attribute; then, that
object (a list in the case of this example) gets told to change
its 0th element to 3. There is no way for Body to get in the
way of the second message, except by doing what I said - adding
a wrapper around the list so that it _can_ get in the way.

This doesn't require the list to know anything about Body, though.
_Body_ should apply the wrapper.

For example, new-style:

class Body(object):
def get_pos(self):
return ChangeNoticingListWrapper(self._pos)
def set_pos(self, value):
self._pos = value
pos = property(get_pos, set_pos)
def __init__(self, pos):
self.pos = pos

class ChangeNoticingListWrapper(object):
def __init__(self, wrapped):
self.wrapped = wrapped
def __setitem__(self, index, value):
print 'Changing', index, 'to', value
self.wrapped[index] = value

b = Body([1, 2])
b.pos[0] = 3

Jean-Paul

Mangabasi

unread,
Dec 21, 2007, 1:30:31 PM12/21/07
to
On Dec 21, 1:11 pm, Jean-Paul Calderone <exar...@divmod.com> wrote:


Hi Jean-Paul,

I see what you are saying but my contrived example is not doing a good
job explaining the real issue I am having here.

When you say "The Body gets asked for the value of the attribute" that
means that Body's __dict__ is being asked to provide a value
corresponding to its 'pos' key, right? Now I may want to ask a more
specific question here. How do I modify Body's __dict__ so that I can
perform a task when __getitem__ method of the __dict__ is called?

Jean-Paul Calderone

unread,
Dec 21, 2007, 1:43:38 PM12/21/07
to pytho...@python.org

You can't do that. Fortunately, there's no reason to. Changing the
behavior of __getitem__ on the __dict__ of a Body instance would let
you address the exact same use-case as a property or a __getattr__
implementation lets you address. Maybe you are asking whether there
is a special method called __getitem__ which is invoked when attributes
are looked up? If so, yes, there is. You can also use a property, as
in the example I gave.

None of that has anything to do with looking up values in the result of
the attribute lookup, though. For example, it doesn't help you change
the behavior of "pos[0]".

Jean-Paul

Bruno Desthuilliers

unread,
Dec 21, 2007, 4:46:33 PM12/21/07
to
Mangabasi a écrit :
(snip)

>
> When you say "The Body gets asked for the value of the attribute" that
> means that Body's __dict__ is being asked to provide a value
> corresponding to its 'pos' key, right?

Wrong. That means that attribute lookup rules are invoked, which may *or
not* end up calling the Body *intance*'s __dict__ __setitem__ method to
be called. Anyway:

> Now I may want to ask a more
> specific question here. How do I modify Body's __dict__ so that I can
> perform a task when __getitem__ method of the __dict__ is called?

This still won't solve your problem. What you want to trace are method
calls on the objects returned by attribute lookup on instances of Body -
not calls to instances __dict__ methods. In your example, what you want
to trace are calls to pos.__getitem__, not calls to b.__dict__.__getitem__.

And if you what you really want is to trace any mutator call to any
arbitrary object that may become an attribute of b, then I'm afraid
you're in for a hard time...

Mangabasi

unread,
Dec 21, 2007, 5:39:22 PM12/21/07
to
On Dec 21, 4:46 pm, Bruno Desthuilliers

Now I am curious about these "attribute lookup rules".
My understanding was:

When an attribute name is accessed in an instance

First __getattribute__(self, name) is called. This guy looks into the
instance's dictionary for the name, if not found then looks into the
parent class's dictionary, if not found searches the base classes, if
not found checks __getattr__, still not found raises an
AttributeError.

Does this look right? If not, are these attribute lookup rules
documented somewhere? I have not been paying much attention to the
new style classes, have things changed with the introduction of these
new style classes?

Bruno Desthuilliers

unread,
Dec 21, 2007, 6:13:07 PM12/21/07
to
Mangabasi a écrit :

> On Dec 21, 4:46 pm, Bruno Desthuilliers
> <bdesth.quelquech...@free.quelquepart.fr> wrote:
>
>>Mangabasi a écrit :
>>(snip)
>>
>>
>>
>>
>>>When you say "The Body gets asked for the value of the attribute" that
>>>means that Body's __dict__ is being asked to provide a value
>>>corresponding to its 'pos' key, right?
>>
>>Wrong. That means that attribute lookup rules are invoked, which may *or
>>not* end up calling the Body *intance*'s __dict__ __setitem__ method to
>>be called. Anyway:
>>
>>
>>> Now I may want to ask a more
>>>specific question here. How do I modify Body's __dict__ so that I can
>>>perform a task when __getitem__ method of the __dict__ is called?
>>
>>This still won't solve your problem. What you want to trace are method
>>calls on the objects returned by attribute lookup on instances of Body -
>>not calls to instances __dict__ methods. In your example, what you want
>>to trace are calls to pos.__getitem__, not calls to b.__dict__.__getitem__.
>>
>>And if you what you really want is to trace any mutator call to any
>>arbitrary object that may become an attribute of b, then I'm afraid
>>you're in for a hard time...
>
>
> Now I am curious about these "attribute lookup rules".
> My understanding was:
>
> When an attribute name is accessed in an instance
>
> First __getattribute__(self, name) is called.

Which is itself resolved first (methods being attributes) !-)

__getattribute__ can itself be overloaded, so the only thing you can bet
is that the first __getattribute__ method found in the mro will be
called. All the rest is the *default* lookup rule:

> This guy looks into the
> instance's dictionary for the name,

Yes.

> if not found then looks into the
> parent class's dictionary,

You mean : the class's dictionnary.

> if not found searches the base classes, if
> not found checks __getattr__,

and invoke it if found

> still not found raises an
> AttributeError.

> Does this look right?

Given the above corrections, mostly, yes. Note that the first one
already makes a big difference. Also, it doesn't take into accounts
__slots__ and descriptors. Let's ignore slots for the moment - we still
have descriptors (property, functions, and other computed attributes),
which have their __get__ method invoked when looked up.

> If not, are these attribute lookup rules
> documented somewhere? I have not been paying much attention to the
> new style classes, have things changed with the introduction of these
> new style classes?

Well, yes, somewhat. Else we wouldn't have them, isn't it ?

Anyway, wrt/ your problem, wether you use old-style or new-style classes
won't make much difference: hooking into Body's instances attribute
lookup won't let you trace attribute lookup on objects that are
themselves attributes of a Body instance (hmmm, not very clear,
sorry...re-reading it slowly, it should make sens. Hopefully...).

Reply all
Reply to author
Forward
0 new messages