Add Properties to Instances?

Bengt Richter bokr at oz.net
Mon Mar 14 23:18:41 CET 2005


On 14 Mar 2005 13:07:29 -0800, "Martin Miller" <ggrp1.20.martineau at dfgh.net> wrote:

>Bengt Richter wrote, in part:
>> On 14 Mar 2005 01:19:23 -0800, "Martin Miller"
><ggrp1.20.martineau at dfgh.net>
>> wrote, in part:
>> >What still puzzles me, though, is why all the above to make
>properties
>> >work on instances is necessary in the first place. It's certainly
>not
>> >clear (to me) from what is said in the How-to at:
>> >>
>>
>>http://users.rcn.com/python/download/Descriptor.htm#invoking-descriptors
>> >I suspect that it may be simply a performance issue, in other words,
>it
>> >was considered too slow to check for instance property/discriptors
>--
>> >although *why* is not clear to me.
>>
>> I suspect the desired semantics re precedence have evolved to make
>normal
>> programs easily implementable, and that probably drove the
>implementation:
>> """
>> For objects, the machinery is in object.__getattribute__ which
>transforms
>> b.x into type(b).__dict__['x'].__get__(b, type(b)).
>> The implementation works through a precedence chain that gives (my
>added
>> [1,2,3])
>>
>> [1] data descriptors priority over instance variables,
>> [2] instance variables priority over non-data descriptors,
>> [3] and assigns lowest priority to __getattr__ if provided.
>>
>> The full C implementation can be found in PyObject_GenericGetAttr()
>in
>> Objects/object.c.
>> """
>
>I haven't examined the C code in Objects/object.c to see *how* the
>semantics are implemented because that's not really the point...which
>is the descriptions of what's suppose to happen don't seem to match
>what actually does. To illustrate, consider:
>> class Foobar(object):
>>     pass
>>
>> def myget(self, obj, type=None):
>>     return 42
>>
>> def myset(self, value):
>>     raise AttributeError("this is a read-only property")
>>
>> foobar = Foobar()
>> foobar.x = property(myget, myset)
>>
>> print "foobar.x:", foobar.x
>
>Which prints:
>> foobar.x: <property object at 0x00AE5850>
>
>Ignoring "why" issue, my question becomes:
>
>foobar.x is a data descriptor property, however object.__getattribute__
It is a property, and it has the requisite methods (__get__ and __set__) to be
a data descriptor, but it is not a data descriptor unless it is visible as
an attribute of type(foobar), which foobar.x is not.

>does *not* seem to be treating it as such and handling it the way
>described either by you or in what is written in the how-to.
>Specifically the statements that:
>> For objects, the machinery is in object.__getattribute__ which
>transforms
>> b.x into type(b).__dict__['x'].__get__(b, type(b)).
>
>This doesn't seem to be occuring. Am I missing something?
>
I think so. Following your example:

 >>> class Foobar(object):
 ...     pass
 ...
 >>> def myget(self, obj, type=None):
 ...     return 42
 ...
 >>> def myset(self, value):
 ...     raise AttributeError("this is a read-only property")
 ...
 >>> foobar = Foobar()
 >>> foobar.x = property(myget, myset)
 >>> print "foobar.x:", foobar.x
 foobar.x: <property object at 0x02EF0644>
 >>>
 >>> type(foobar).__dict__['x']
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 KeyError: 'x'

meaning
   b.x into type(b).__dict__['x'].__get__(b, type(b)).
does not apply, and the attempt to get foobar.x falls back to
ordinary attribute access. I.e., foobar.x will be whatever

BTW, I'm not sure type(b).__dict__['x'].__get__(b, type(b))
is really fully correct, since that would not find foobar.x in a Foobar base class:

 >>> class Base(object): pass
 ...
 >>> Base.x = property(myget, myset)
 >>> class Sub(Base): pass
 ...
 >>> sub = Sub()
 >>> sub.x
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 TypeError: myget() takes at least 2 arguments (1 given)

(At least it was found, even if the signature is bad ;-)

But:

 >>> type(sub).__dict__['x']
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 KeyError: 'x'

So the real truth chases down the mro chain before giving up
and falling back to ordinary attribute access, IWT.

Regards,
Bengt Richter



More information about the Python-list mailing list