Bengt Richter wrote: > On 14 Mar 2005 13:07:29 -0800, "Martin Miller" <[EMAIL PROTECTED]> wrote: > > >Bengt Richter wrote, in part: > >> On 14 Mar 2005 01:19:23 -0800, "Martin Miller" > ><[EMAIL PROTECTED]> > >> 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
Ah, from your comments and explanation I think I finally understand that the description in the docs *is* in fact accurate -- and indirectly, the reason for properties on instances not working by default. Specifically I was not realizing that the "type(b).__dict__" in the statement: > b.x is tranformed into type(b).__dict__['x'].__get__(b, type(b)) implys that no check for 'x' in made in "b.__dict__". Now I'm back to wondering "why" because it would be relatively easy to check for a descriptor in the instance dictionary first -- and would be more "Pythonic", IMHO, since instances can generally augment (or override) that which is defined by their class. Thank you for bearing with me on this somewhat obtuse follow-on question regarding properties and descriptors (and to the everyone else for their suggestions for work-arounds). Best regards, Martin P.S. After looking at the implementation of PyObject_GenericGetAttr() in Objects/object.c, I believe your observation about the "type(b).__dict__['x'].__get__(b, type(b))" not being fully correct is true. Instead, there appears to be a search being done through the mro base classes of type(b). Perhaps that fact could be used to create "mix-in" style classes with the desired properties... -- http://mail.python.org/mailman/listinfo/python-list