Re: Dynamic use of property() fails
Gabriel Genellina [EMAIL PROTECTED] writes: The magic happens when the descriptor is found in the *class*, not in the instance. I think it's detailed in Hettinger's document. The document is wrong here: Alternatively, it is more common for a descriptor to be invoked automatically upon attribute access. For example, obj.d looks up d in the dictionary of obj. If d defines the method __get__, then d.__get__(obj) is invoked according to the precedence rules listed below. This sounds plausible and might have led Andrew to believe that his code should work. It should instead say in the dictionary of obj's type or something to that effect. I asked Raymond about it some time ago, and he agreed that it's an error, but he apparently didn't get around to fixing it. The actual code examples in the document are, of course, correct. -- http://mail.python.org/mailman/listinfo/python-list
Re: Dynamic use of property() fails
andrew cooke [EMAIL PROTECTED] writes: This is my first attempt at new classes and dynamic python, so I am probably doing something very stupid... After reading the how-to for descriptors at http://users.rcn.com/python/download/Descriptor.htm I decided I would make an object that returns attributes on read, but on setting calls an arbitrary function. My code looks like: class ActiveDAO(object): def __init__(self): self.__values__ = {} def add_field(self, name, value, on_change): self.__values__[name] = value def get(self): return self.__values__[name] def set(self, new_value): self.__values__[name] = on_change(new_value) def delete(self): raise AttributeError self.__dict__[name] = property(get, set, delete) As others explained, descriptors are called for descriptors found in class attributes, not in ones in instance attributes. Calling them for the latter would be dangerous because it might accidentally invoke magic whenever you store the wrong kind of object in an instance attribute. Also, in many cases (slots), instance property access is *implemented* using class property descriptors, so calling descriptors on objects retrieved from the instance would mean that the descriptor would have to be invoked twice. However, if you know what you're doing, you can simply customize your class's __getattribute__ to do what *you* want for your objects. For example: def __getattribute__(self, name): dict = object.__getattribute__(self, '__dict__') # self.__dict__ would infloop if name in dict: o = dict[name] # call the descriptor even if found in an object in __dict__ if hasattr(o, '__get__'): return o.__get__(self, type(self)) return o return object.__getattribute__(self, name) With that addition: dao = ActiveDAO() dao.add_field('name', 'value', lambda _: None) dao.name 'value' dao.__dict__['name'] property object at 0xb7d53284 -- http://mail.python.org/mailman/listinfo/python-list
Re: Dynamic use of property() fails
andrew cooke a écrit : Hi, This is my first attempt at new classes and dynamic python, so I am probably doing something very stupid... After reading the how-to for descriptors at http://users.rcn.com/python/download/Descriptor.htm I decided I would make an object that returns attributes on read, but on setting calls an arbitrary function. My code looks like: class ActiveDAO(object): def __init__(self): self.__values__ = {} __names__ are reserved for the Python implementation itself. Use _names for 'protected' attributes. def add_field(self, name, value, on_change): self.__values__[name] = value def get(self): return self.__values__[name] def set(self, new_value): self.__values__[name] = on_change(new_value) def delete(self): raise AttributeError self.__dict__[name] = property(get, set, delete) However, when I try to use this (in a test) with code like: dao = ActiveDAO() dao.add_field(name, value, lambda _: None) assertEqual(dao.name, value) I get a failure because lookup of the attribute is returning property object at 0x6b8910. That is quite reasonable, but I was under the expression that some magic was supposed to happen, as described in the document referenced above! Please can someone explain why there is no magic? :o( Others already answered this. The canonical solution is to use a custom descriptor instead of a property: class Field(object): def __init__(self, name, onchange): self.name = name self.onchange = onchange def __get__(self, instance, cls): if instance is None: # called on the class return self # called on instance return instance._values[self.name] def __set__(self, instance, value): instance._values[name] = self.onchange(value) class ActiveDAO(object): def __init__(self): self._values = [] class Person(ActiveDAO): name = Field('firstname', lambda v: v.strip().capitalize()) age = Field('age', lambda v : int(v)) Now you may want to search here or in the cookbook to learn how to: - dynamically create new classes - avoid having to repeat the name of the field (usually done using a metaclass and a two-stages initialisation of Field objects) HTH -- http://mail.python.org/mailman/listinfo/python-list
Re: Dynamic use of property() fails
Hrvoje Niksic a écrit : (snip) As others explained, descriptors are called for descriptors found in class attributes, not in ones in instance attributes. (snip) However, if you know what you're doing, you can simply customize your class's __getattribute__ to do what *you* want for your objects. op But bear in mind that, beside possible unwanted side-effectn, you'll get a non-negligible performance hit - __getattribute__ being, as the name implies, invoked on each and every attribute lookup. FWIW, I tried this approach once, and quickly came back to a simpler solution. /op (snip code) -- http://mail.python.org/mailman/listinfo/python-list
Re: Dynamic use of property() fails
On Apr 15, 4:06 am, Bruno Desthuilliers bruno. [EMAIL PROTECTED] wrote: The canonical solution is to use a custom descriptor instead of a property: class Field(object): def __init__(self, name, onchange): self.name = name self.onchange = onchange def __get__(self, instance, cls): if instance is None: # called on the class return self # called on instance return instance._values[self.name] def __set__(self, instance, value): instance._values[name] = self.onchange(value) class ActiveDAO(object): def __init__(self): self._values = [] class Person(ActiveDAO): name = Field('firstname', lambda v: v.strip().capitalize()) age = Field('age', lambda v : int(v)) i tried code very similar after reading the first replies and found that it did not work as expected on setting. for example, in person = Person() person.age = 27 age is set in the instance's dictionary (as 27; the descriptor is not called), which then shadows the definition of age in the class dictionary. my understanding was that the descriptor is only called in the class context, so would be called if, say, a subclass tried to redefine age. but maybe i am still confused. i am about to go to sleep. i guess i will try your code exactly tomorrow, but it looks very close to mine which showed this problem. are you sure your solution works? thanks, andrew -- http://mail.python.org/mailman/listinfo/python-list
Re: Dynamic use of property() fails
ignore that - i was mistaken (my test was too complex). the problem seems to be that the attribute is deleted even though __delete__ is defined. i'll look at it tomorrow. thanks again, andrew On Apr 15, 4:50 am, andrew cooke [EMAIL PROTECTED] wrote: i tried code very similar after reading the first replies and found that it did not work as expected on setting. for example, in person = Person() person.age = 27 age is set in the instance's dictionary (as 27; the descriptor is not called), which then shadows the definition of age in the class dictionary. my understanding was that the descriptor is only called in the class context, so would be called if, say, a subclass tried to redefine age. but maybe i am still confused. i am about to go to sleep. i guess i will try your code exactly tomorrow, but it looks very close to mine which showed this problem. are you sure your solution works? thanks, andrew -- http://mail.python.org/mailman/listinfo/python-list
Re: Dynamic use of property() fails
OK, fixed my bug - it does work. Now sleep... Thanks again, Andrew -- http://mail.python.org/mailman/listinfo/python-list
Re: Dynamic use of property() fails
andrew cooke a écrit : On Apr 15, 4:06 am, Bruno Desthuilliers bruno. [EMAIL PROTECTED] wrote: The canonical solution is to use a custom descriptor instead of a property: (snip code) i tried code very similar after reading the first replies and found that it did not work as expected on setting. for example, in person = Person() person.age = 27 age is set in the instance's dictionary (as 27; the descriptor is not called), which then shadows the definition of age in the class dictionary. Are you sure your Person class is a new-style one (inheriting, directly or not, from object) ? The descriptor protocol doesn't work properly on old-style classes, with *exactly* the symptom you describe here (setter is not invoked, property get shadowed by an instance attribute) my understanding was that the descriptor is only called in the class context, The descriptor protocol is only invoked on class attributes. so would be called if, say, a subclass tried to redefine age. but maybe i am still confused. Possibly. i am about to go to sleep. i guess i will try your code exactly tomorrow, but it looks very close to mine which showed this problem. are you sure your solution works? Yes - minus a couple (unrelated) typos ('name' instead of 'self.name' in Field.__set__, and 'self._values = []' instead of 'self._values = {}' in ActiveDAO.__init__). Here's the corrected one: class Field(object): def __init__(self, name, onchange): self.name = name self.onchange = onchange def __get__(self, instance, cls): if instance is None: # called on the class return self # called on instance return instance._values[self.name] def __set__(self, instance, value): instance._values[self.name] = self.onchange(value) class ActiveDAO(object): def __init__(self): self._values = {} class Person(ActiveDAO): name = Field('name', lambda v: v.strip().capitalize()) age = Field('age', lambda v : int(v)) -- http://mail.python.org/mailman/listinfo/python-list
Re: Dynamic use of property() fails
Bruno Desthuilliers [EMAIL PROTECTED] writes: However, if you know what you're doing, you can simply customize your class's __getattribute__ to do what *you* want for your objects. op But bear in mind that, beside possible unwanted side-effectn, you'll get a non-negligible performance hit - __getattribute__ being, as the name implies, invoked on each and every attribute lookup. That's unavoidable, though -- whatever you do to customize your class in Python, the result will be slower than the C code built into Python. Fortunately, not every object is performance-critical. In this case, __getattribute__ buys you per-instance customization not otherwise available. The code you posted is probably more efficient, but at the cost of losing the ability to customize specific instances of the class. -- http://mail.python.org/mailman/listinfo/python-list
Re: Dynamic use of property() fails
Hrvoje Niksic a écrit : Bruno Desthuilliers [EMAIL PROTECTED] writes: However, if you know what you're doing, you can simply customize your class's __getattribute__ to do what *you* want for your objects. op But bear in mind that, beside possible unwanted side-effectn, you'll get a non-negligible performance hit - __getattribute__ being, as the name implies, invoked on each and every attribute lookup. That's unavoidable, though -- whatever you do to customize your class in Python, the result will be slower than the C code built into Python. This one customization hook is probably the most sensible still. Fortunately, not every object is performance-critical. In this case, __getattribute__ buys you per-instance customization not otherwise available. The code you posted is probably more efficient, but at the cost of losing the ability to customize specific instances of the class. You could as well just customize __setattr__/__getattr__ (which is what was done before new-style classes and descriptors). But my own experience is that the very few times I thought I had a need for per-instance descriptors, I found other solutions that were certainly easier to grasp and maintain on the long term. Not that I would not be able to deal with per-instance descriptors, but so far the cost outweighted the benefits IMHO. Now YMMV of course !-) -- http://mail.python.org/mailman/listinfo/python-list
Re: Dynamic use of property() fails
En Mon, 14 Apr 2008 22:43:39 -0300, andrew cooke [EMAIL PROTECTED] escribió: This is my first attempt at new classes and dynamic python, so I am probably doing something very stupid... After reading the how-to for descriptors at http://users.rcn.com/python/download/Descriptor.htm I decided I would make an object that returns attributes on read, but on setting calls an arbitrary function. My code looks like: class ActiveDAO(object): def __init__(self): self.__values__ = {} def add_field(self, name, value, on_change): self.__values__[name] = value def get(self): return self.__values__[name] def set(self, new_value): self.__values__[name] = on_change(new_value) def delete(self): raise AttributeError self.__dict__[name] = property(get, set, delete) However, when I try to use this (in a test) with code like: dao = ActiveDAO() dao.add_field(name, value, lambda _: None) assertEqual(dao.name, value) I get a failure because lookup of the attribute is returning property object at 0x6b8910. That is quite reasonable, but I was under the expression that some magic was supposed to happen, as described in the document referenced above! The magic happens when the descriptor is found in the *class*, not in the instance. I think it's detailed in Hettinger's document. Do you actually want per-instance defined properties? __special__ names are reserved for Python internal usage; don't use them. Implementation-only attributes (private ones) are spelled with a single underscore. -- Gabriel Genellina -- http://mail.python.org/mailman/listinfo/python-list
Re: Dynamic use of property() fails
[Gabriel:] The magic happens when the descriptor is found in the *class*, not in the instance. I think it's detailed in Hettinger's document. Do you actually want per-instance defined properties? ah! ok. yes, you're right: i want instance values but class properties, so i'll rethink things. thanks very much! __special__ names are reserved for Python internal usage; don't use them. Implementation-only attributes (private ones) are spelled with a single underscore. ah, yes, sorry about that. thanks again for the quick reply, andrew -- http://mail.python.org/mailman/listinfo/python-list