[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