
On 1 December 2017 at 21:04, Ronald Oussoren <ronaldoussoren@mac.com> wrote:
The second question is more a design question: what’s the better design, having __getdescriptor__ as a class method on classes or as method on metaclasses? Either one would work, but a class method appears to be easier to use and with the introduction of __init_subclass__ there is a precedent for going for a class method.
The current PEP claims that a method on a metaclass would be better to avoid subtle problems, but ignores the conceptual cost of adding a metaclass. The subtle problem is that a class can have two direct superclasses with a __getdescriptor__ when using multiple inheritance, but that can already be an issue for other methods and that currently includes __getattribute__ for most of not all usecases where __getdescriptor__ would be useful.
I think it's having it being a method on the metaclass that creates the infinite regress Mark was worried about: since type's metaclass *is* type, if "__getdescriptor__" is looked up as a regular descriptor in its own right, then there's no base case to terminate the recursive lookup. By contrast, defining it as a class method opens up two options: 1. Truly define it as a class method, and expect implementors to call super().__getdescriptor__() if their own lookup fails. I think this will be problematic and a good way to get the kinds of subtle problems that prompted you to initially opt for the metaclass method. 2. Define it as a class method, but have the convention be for the *caller* to worry about walking the MRO, and hence advise class implementors to *never* call super() from __getdescriptor__ implementations (since doing so would nest MRO walks, and hence inevitably have weird outcomes). Emphasise this convention by passing the current base class from the MRO as the second argument to the method. The reason I'm liking option 2 is that it leaves the existing __getattribute__ implementations fully in charge of the MRO walk, and *only* offers a way to override the "base.__dict__[name]" part with a call to "base.__dict__['__getdescriptor__'](cls, base, name)" instead. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia