[Python-Dev] PEP 447 (type.__getdescriptor__)

Ronald Oussoren ronaldoussoren at mac.com
Wed Aug 5 09:52:18 CEST 2015


> On 26 Jul 2015, at 14:18, Mark Shannon <mark at hotpy.org> wrote:
> 
>> On 26 July 2015 at 10:41 Ronald Oussoren <ronaldoussoren at mac.com> wrote:
>> 
>> 
>> 
>>> On 26 Jul 2015, at 09:14, Ronald Oussoren <ronaldoussoren at mac.com> wrote:
>>> 
>>> 
>>>> On 25 Jul 2015, at 17:39, Mark Shannon <mark at hotpy.org
>>>> <mailto:mark at hotpy.org>> wrote:
>>>> 
>>>> Hi,
>>>> 
>>>> On 22/07/15 09:25, Ronald Oussoren wrote:> Hi,
>>>>> 
>>>>> Another summer with another EuroPython, which means its time again to 
>>>>> try to revive PEP 447…
>>>>> 
>>>> 
>>>> IMO, there are two main issues with the PEP and implementation.
>>>> 
>>>> 1. The implementation as outlined in the PEP is infinitely recursive, since
>>>> the
>>>> lookup of "__getdescriptor__" on type must necessarily call
>>>> type.__getdescriptor__.
>>>> The implementation (in C) special cases classes that inherit
>>>> "__getdescriptor__"
>>>> from type. This special casing should be mentioned in the PEP.
>>> 
>>> Sure.  An alternative is to slightly change the the PEP: use
>>> __getdescriptor__ when
>>> present and directly peek into __dict__ when it is not, and then remove the
>>> default
>>> __getdescriptor__. 
>>> 
>>> The reason I didn’t do this in the PEP is that I prefer a programming model
>>> where
>>> I can explicitly call the default behaviour. 
>> 
>> I’m not sure there is a problem after all (but am willing to use the
>> alternative I describe above),
>> although that might be because I’m too much focussed on CPython semantics.
>> 
>> The __getdescriptor__ method is a slot in the type object and because of that
>> the
>> normal attribute lookup mechanism is side-stepped for methods implemented in
>> C. A
>> __getdescriptor__ that is implemented on Python is looked up the normal way by
>> the 
>> C function that gets added to the type struct for such methods, but that’s not
>> a problem for
>> type itself.
>> 
>> That’s not new for __getdescriptor__ but happens for most other special
>> methods as well,
>> as I noted in my previous mail, and also happens for the __dict__ lookup
>> that’s currently
>> used (t.__dict__ is an attribute and should be lookup up using
>> __getattribute__, …)
> 
> 
> "__getdescriptor__" is fundamentally different from "__getattribute__" in that
> is defined in terms of itself.
> 
> object.__getattribute__ is defined in terms of type.__getattribute__, but
> type.__getattribute__ just does 
> dictionary lookups.

object.__getattribute__ is actually defined in terms of type.__dict__ and object.__dict__. Type.__getattribute__ is at best used to to find type.__dict__.

> However defining type.__getattribute__ in terms of
> __descriptor__ causes a circularity as
> __descriptor__ has to be looked up on a type.
> 
> So, not only must the cycle be broken by special casing "type", but that
> "__getdescriptor__" can be defined
> not only by a subclass, but also a metaclass that uses "__getdescriptor__" to
> define  "__getdescriptor__" on the class.
> (and so on for meta-meta classes, etc.)

Are the semantics of special methods backed by a member in PyTypeObject part of Python’s semantics, or are those CPython implementation details/warts? In particular that such methods are access directly without using __getattribute__ at all (or at least only indirectly when the method is implemented in Python).  That is:

>>> class Dict (dict):
...    def __getattribute__(self, nm):
...       print("Get", nm)
...       return dict.__getattribute__(self, nm)
... 
>>> 
>>> d = Dict(a=4)
>>> d.__getitem__('a')
Get __getitem__
4
>>> d['a']
4
>>> 

(And likewise for other special methods, which amongst others means that neither __getattribute__ nor __getdescriptor__ can be used to dynamicly add such methods to a class)

In my proposed patch I do special case “type”, but that’s only intended as a (for now unbenchmarked) speed hack.  The code would work just as well without the hack because the metatype’s  __getdescriptor__ is looked up directly in the PyTypeObject on the C level, without using __getattribute__ and hence without having to use recursion.

BTW. I wouldn’t mind dropping the default “type.__getdescriptor__” completely and reword my proposal to state that __getdescriptor__ is used when present, and otherwise __dict__ is accessed directly.  That would remove the infinite recursion, as all metaclass chains should at some end up at “type” which then wouldn’t have a “__getdescriptor__”.   

The reason I added “type.__getdescriptor__” is that IMHO gives a nicer programming model where you can call the superclass implementation in the implementation of __getdescriptor__ in a subclass.  Given the minimal semantics of “type.__getdescriptor__”  loosing that wouldn’t be too bad to get a better object model.

Ronald

> 
> Cheers,
> Mark
> _______________________________________________
> Python-Dev mailing list
> Python-Dev at python.org
> https://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe: https://mail.python.org/mailman/options/python-dev/ronaldoussoren%40mac.com

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20150805/47086144/attachment-0001.html>


More information about the Python-Dev mailing list