[Python-ideas] The Descriptor Protocol...

Raymond Hettinger raymond.hettinger at gmail.com
Wed Mar 2 17:10:15 CET 2011


On Mar 2, 2011, at 1:05 AM, Martin Chilvers wrote:

> G'day!
> 
> Please excuse me if I have missed something obvious, but I have a question about the implementation of the descriptor protocol, and more specifically about the arguments passed to the '__get__' and '__set__' methods.
> 
> According to Raymond Hettinger's "How-To Guide for Descriptors" at:-
> 
> http://users.rcn.com/python/download/Descriptor.htm#invoking-descriptors
> 
> The *pseudo* implementation of __getattribute__ is as follows:-
> 
> def __getattribute__(self, key):
>    "Emulate type_getattro() in Objects/typeobject.c"
>    v = object.__getattribute__(self, key)
>    if hasattr(v, '__get__'):
>       return v.__get__(None, self)
>    return v
> 
> As mentioned above, this is obviously only pseudo-Python, but it serves to illustrate my question which is, why isn't the 'key' argument passed through to the '__get__' (and similarly, '__set__') methods?

I've gotten this question a couple times.   There usual answers are:

* Why? Because Guido implemented it that way ;-)

* Why did he pick a signature that dropped the "key"?  Mostly likely, it is because none of the use-cases he had in mind required it.  The keyless API suffices to implement property(), super(), classmethod(), staticmethod(), bound method, __slots__, various features of new-style classes, and a reimplementation old-style classes.



> 2) It makes the implementation of some descriptor based tools much uglier. Take for example a type specification tool, we might have something like:-
> 
> class Person(object):
>    name    = Str()
>    address = Str()
> 
> where 'Str' is a descriptor. It would be nice to know in the '__get__' and '__set__' methods of 'Str' which attribute is being accessed. Of course, I can get around this by either:-
> 
> a) using a metaclass to harvest the descriptors and set the attribute name. This is fine, but:-
>  - it forces me to use a metaclass ;^)
>  - it means that I can't share descriptors because they are bound to
>    a particular attribute name which has obvious scaleability
>    implications.
> 
> b) make the developer duplicate the attribute name when constructing the descriptor:-
> 
> class Person(object):
>    name    = Str('name')
>    address = Str('address')
> 
> which, well, just smells, and conflicts with step 3 of TDD ;^)

Yes, option b is the usual way to do it (there are a number of recipes that use this approach).

For the most part, that shouldn't be an unfamiliar pattern to Python developers.  For example, named tuples repeat the name:
   Point = namedtuple('Point', ('x', 'y')).   

Sometimes the language provides syntax to do the work for us, like "class A: ..." instead of "A = type('A', (), {})", but for other situations, it isn't unusual to pass in a name as a string literal so that an object will know its own name.

For Python 4, perhaps you can convince Guido to add a key argument to the signature for __get__ and __set__.  It would make a handful of recipes a bit prettier at the expense of being a total waste for all the other use cases that don't need it.

I think everyone who writes a descriptor that needs the "key" will chafe at the current design.  It bugged me at first, but the workaround is easy and after a while it doesn't seem as bothersome.


Raymond



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20110302/51071e38/attachment.html>


More information about the Python-ideas mailing list