[Python-ideas] PEP 447: Adding type.__getdescriptor__

Ronald Oussoren ronaldoussoren at mac.com
Fri Dec 1 04:15:35 EST 2017



> On 1 Dec 2017, at 07:01, Nick Coghlan <ncoghlan at gmail.com> wrote:
> 
> On 1 December 2017 at 01:23, Ronald Oussoren <ronaldoussoren at mac.com> wrote:
>> 1) Last time around Mark Shannon worried that this introduces infinite
>> recursion in the language itself (in my crummy summary, please read this
>> message to get the real concern
>> <https://mail.python.org/pipermail/python-dev/2015-July/140938.html>).  Is
>> this truly a problem?  I don’t think there is a problem, but I’m worried
>> that I don’t fully understand Mark’s concerns.
>> 
>> 2) PEP 487 introduced __init_subclass__ as a class method to avoid having to
>> write a metaclass for a number of use cases.  My PEP currently does require
>> a metaclass, but it might be nicer to switch to a regular class method
>> instead (like __init_subclass__).
> 
> I think the second point there may actually allow you to resolve the
> first one, by way of making `__getdescriptor__` an optional override
> of the typical lookup algorithm. That is:
> 
>    def _PyType_Lookup(tp, name):
> 
>        # New optional override for descriptor lookups
>        try:
>            # Ordinary attribute lookup, *NOT* a descriptor lookup
>            getdesc = tp.__getdescriptor__
>        except AttributeError:
>            pass
>        else:
>            return getdesc(name)
> 
>        # Default algorithm used in the absence of an override
>        mro = tp.mro()
>        assert isinstance(mro, tuple)
> 
>        for base in mro:
>           assert isinstance(base, type)
> 
>           try:
>               return base.__dict__[name]
>           except KeyError:
>               pass
> 
>        return None
> 
> If you did go this way, then we'd want to add a
> "types.getdescriptor(cls, name)" API to expose _PyType_Lookup at the
> Python layer, since "getattr(type(obj), name)" wouldn't be an accurate
> emulation of the algorithm any more.

Maybe, but how would this work with super()? Super walks the MRO of type of the instance, but skips the class on the MRO.  This is not equivalent to walking the MRO of the second class on the MRO when you use multiple inheritance,

This also has some other disadvantages. The first is that tp.__getdescriptor__ would replace the default behaviour for the entire MRO and it would be possible to have different behavior for classes on the MRO. The second, minor. one is that __getdescriptor__ would have to reimplement the default logic of walking the MRO, but that logic is fairly trivial.

BTW. getattr(type(obj), name) is not an accurate emulation of _PyType_Lookup even now, thanks to metaclasses. In particular:

```
class A_meta (type):
    @property
    def description(self): return "meta description"

    @description.setter
    def description(self, v):
        raise RuntimeError

class A (metaclass=A_meta):

    @property
    def description(self): return "description"

    @description.setter
    def description(self, v):
        raise RuntimeError


a = A()

print(A.description)                    # prints “meta description"
print(a.description)                    # prints “description"
print(getattr(type(a), 'description’))  # prints “meta description"
```

The setter definitions are necessary to ensure that the properties are data descriptors, which are handled differently than function descriptors by __getattribute__.

Ronald

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20171201/dc353f7a/attachment-0001.html>


More information about the Python-ideas mailing list