[Python-Dev] Descriptor write-up [Draft: Please comment]

Phillip J. Eby pje@telecommunity.com
2003年6月01日 06:32:36 -0400


At 04:32 AM 6/1/03 -0400, Raymond Hettinger wrote:
>In the spirit of Michele Simionato's write-up on method resolution order, 
>I've written up almost everything I know about
>descriptors:
>http://tinyurl.com/d63d
>>Key features:
>* Defines a descriptor
>* Shows the protocol and how it is called
>* Shows a handmade descriptor
>* Then covers functions, properties, static and class methods
>* Gives examples of each
>* Suggests when each would be useful
>* Gives a pure python definition of each
>* Provides exact references into C source to save efforts in hunting down 
>details
>>I would like to hammer this into something really useful. So, any and all 
>suggestions are welcome.

It would be a good idea to add some information about "data" and "non-data" 
descriptors, and the differences of how their attribute lookup is 
processed. I recently posted here about the "attribute lookup process" or 
something to that effect, which covered this. Understanding data vs. 
non-data descriptors is important if you want to do pretty much anything 
with descriptors beyond what property() does.
This section:
"Alternatively, a descriptor is invoked automatically upon attribute 
access. For example, obj.a looks up a in the dictionary of obj. If obj 
defines the method __get__, then it is automatically invoked if obj is a 
new style class or object."
isn't accurate. I think you meant to say that if 'a' defines __get__, then 
it's invoked. But this isn't accurate either. If obj.__dict__ has an 'a' 
entry, and the descriptor is a non-data descriptor, __get__ will be ignored 
and the contents of obj.__dict__ will be returned. (One interesting effect 
of this is that you can create a non-data descriptor with a __get__ that 
performs a computation and stashes the result in the instance dictionary - 
from then on the computed value is returned.)
The Python code you have for the algorithm is also incorrect. Here's a 
more accurate depiction of the process. It's not a straight translation of 
PyGeneric_GetAttr, but an attempt to do as you have done, i.e. write a pure 
Python __getattribute__ with equivalent semantics.
 def __getattribute__(self, key):
 dict = object.__getattribute__(self, '__dict__')
 try:
 # Does the class have a descriptor for this attribute?
 descr = getattr(type(self),key)
 except AttributeError:
 # No, just use what's in the dictionary
 try:
 return dict[key]
 except KeyError:
 raise AttributeError
 if hasattr(descr,'__set__') and hasattr(descr,'__get__'):
 # It's a data descriptor, so use the get method
 return descr.__get__(self,type(self))
 # non-data descriptor, use __dict__ first, *then* __get__
 try:
 return dict[key]
 except KeyError:
 if hasattr(descr, '__get__'):
 return descr.__get__(self, type(self))
 # not a descriptor, return it as-is
 return descr
As you can see, it's a bit more complex than your writeup implies. :)

AltStyle によって変換されたページ (->オリジナル) /