
On 1 Dec 2017, at 07:01, Nick Coghlan <ncoghlan@gmail.com> wrote:
On 1 December 2017 at 01:23, Ronald Oussoren <ronaldoussoren@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