> On 1 Dec 2017, at 12:29, Nick Coghlan <ncogh...@gmail.com> wrote: > > On 1 December 2017 at 21:04, Ronald Oussoren <ronaldousso...@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.
But type.__getattribute__ is already special, it cannot be the same as its superclass implementation (because that’s object), and object.__getattribute__ logically uses type.__getattribute__ to get at type.__dict__. Adding __getdescriptor__ in the mix makes type.__getattribute__ a bit messier, but not by much. > > 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. The only subtle problem is having a class using multiple inheritance that uses two __getdescriptor__ implementations from two superclasses, where both do something beyond looking up the name in __dict__. Failing to call super().__getdescriptor__() is similar to failing to do so for other methods. > > 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. But that’s how I already define the method, that is the PEP proposes to change the MRO walking loop to: for cls in mro_list: try: return cls.__getdescriptor__(name) # was cls.__dict__[name] except AttributeError: # was KeyError pass Note that classes on the MRO control how to try to fetch the name at that level. The code is the same for __getdescriptor__ as a classmethod and as a method on the metaclass. I don’t think there’s a good technical reason to pick either option, other than that the metaclass option forces an exception when creating a class that inherits (using multiple inheritance) from two classes that have a custom __getdescriptor__. I’m not convinced that this is a good enough reason to go for the metaclass option. > > 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. Right. That’s why I propose __getdescriptor__ in the first place. This allows Python coders to do extra work (or different work) to fetch an attribute of a specific class in the MRO and works both with regular attribute lookup as well as lookup through super(). The alternative I came up with before writing the PEP is to add a special API that can be used by super(), but that leads to more code duplication as coders would have to implement both __getattribute__ and this other method. I guess another options would be a method that does the work including walking the MRO, but that leads to more boilerplate for users of the API. BTW. I haven’t had a lot of time to work on the implementation. The code in typeobject.c has changed enough that this needs more work than tweaking the patch until it applies cleanly. Ronald _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/