<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Hi,<div class=""><br class=""></div><div class="">Another summer with another EuroPython, which means its time again to try to revive PEP 447…</div><div class=""><br class=""></div><div class="">I’ve just pushes a minor update to the PEP and would like to get some feedback on this, arguably fairly esoteric, PEP.</div><div class=""><br class=""></div><div class="">The PEP proposes to to replace direct access to the class __dict__ in object.__getattribute__ and super.__getattribute__ by calls to a new special method to give the metaclass more control over attribute lookup, especially for access using a super() object.  This is needed for classes that cannot store (all) descriptors in the class dict for some reason, see the PEP text for a real-world example of that.</div><div class=""><br class=""></div><div class="">Regards,</div><div class=""><br class=""></div><div class="">  Ronald</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">The PEP text (with an outdated section with benchmarks removed):</div><div class=""><br class=""></div><div class=""><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">PEP: 447</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Title: Add __getdescriptor__ method to metaclass</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Version: $Revision$</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Last-Modified: $Date$</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Author: Ronald Oussoren <<a href="mailto:ronaldoussoren@mac.com" class="">ronaldoussoren@mac.com</a>></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Status: Draft</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Type: Standards Track</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Content-Type: text/x-rst</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Created: 12-Jun-2013</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Post-History: 2-Jul-2013, 15-Jul-2013, 29-Jul-2013, 22-Jul-2015</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Abstract</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">========</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Currently ``object.__getattribute__`` and ``super.__getattribute__`` peek</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">in the ``__dict__`` of classes on the MRO for a class when looking for</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">an attribute. This PEP adds an optional ``__getdescriptor__`` method to</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">a metaclass that replaces this behavior and gives more control over attribute</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">lookup, especially when using a `super`_ object.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">That is, the MRO walking loop in ``_PyType_Lookup`` and</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">``super.__getattribute__`` gets changed from::</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">     def lookup(mro_list, name):</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">         for cls in mro_list:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">             if name in cls.__dict__:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">                 return cls.__dict__</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">         return NotFound</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">to::</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">     def lookup(mro_list, name):</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">         for cls in mro_list:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">             try:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">                 return cls.__getdescriptor__(name)</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">             except AttributeError:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">                 pass</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">         return NotFound</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The default implemention of ``__getdescriptor__`` looks in the class</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">dictionary::</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">   class type:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">      def __getdescriptor__(cls, name):</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">          try:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">  </span>      return cls.__dict__[name]</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">        </span>  except KeyError:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">       </span>      raise AttributeError(name) from None</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Rationale</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">=========</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">It is currently not possible to influence how the `super class`_ looks</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">up attributes (that is, ``super.__getattribute__`` unconditionally</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">peeks in the class ``__dict__``), and that can be problematic for</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">dynamic classes that can grow new methods on demand.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The ``__getdescriptor__`` method makes it possible to dynamically add</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">attributes even when looking them up using the `super class`_.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The new method affects ``object.__getattribute__`` (and</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">`PyObject_GenericGetAttr`_) as well for consistency and to have a single</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">place to implement dynamic attribute resolution for classes.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Background</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">----------</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The current behavior of ``super.__getattribute__`` causes problems for</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">classes that are dynamic proxies for other (non-Python) classes or types,</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">an example of which is `PyObjC`_. PyObjC creates a Python class for every</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">class in the Objective-C runtime, and looks up methods in the Objective-C</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">runtime when they are used. This works fine for normal access, but doesn't</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">work for access with `super`_ objects. Because of this PyObjC currently</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">includes a custom `super`_ that must be used with its classes, as well as</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">completely reimplementing `PyObject_GenericGetAttr`_ for normal attribute</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">access.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The API in this PEP makes it possible to remove the custom `super`_ and</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">simplifies the implementation because the custom lookup behavior can be</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">added in a central location.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.. note::</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">   `PyObjC`_ cannot precalculate the contents of the class ``__dict__``</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">   because Objective-C classes can grow new methods at runtime. Furthermore</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">   Objective-C classes tend to contain a lot of methods while most Python</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">   code will only use a small subset of them, this makes precalculating</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">   unnecessarily expensive.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The superclass attribute lookup hook</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">====================================</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Both ``super.__getattribute__`` and ``object.__getattribute__`` (or</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">`PyObject_GenericGetAttr`_ and in particular ``_PyType_Lookup`` in C code)</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">walk an object's MRO and currently peek in the class' ``__dict__`` to look up</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">attributes.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">With this proposal both lookup methods no longer peek in the class ``__dict__``</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">but call the special method ``__getdescriptor__``, which is a slot defined</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">on the metaclass. The default implementation of that method looks</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">up the name the class ``__dict__``, which means that attribute lookup is</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">unchanged unless a metatype actually defines the new special method.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Aside: Attribute resolution algorithm in Python</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">-----------------------------------------------</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The attribute resolution proces as implemented by ``object.__getattribute__``</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">(or PyObject_GenericGetAttr`` in CPython's implementation) is fairly</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">straightforward, but not entirely so without reading C code.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The current CPython implementation of object.__getattribute__ is basicly</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">equivalent to the following (pseudo-) Python code (excluding some house keeping and speed tricks)::</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">    def _PyType_Lookup(tp, name):</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">        mro = tp.mro()</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">     </span>assert isinstance(mro, tuple)</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">    </span>for base in mro:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">      </span>   assert isinstance(base, type)</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">    </span>   # PEP 447 will change these lines:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">    </span>   try:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">  </span>       return base.__dict__[name]</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">      </span>   except KeyError:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">      </span>       pass</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">       </span>return None</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">    class object:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">        def __getattribute__(self, name):</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">     </span>    assert isinstance(name, str)</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">       </span>    tp = type(self)</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre"> </span>    descr = _PyType_Lookup(tp, name)</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">   </span>    f = None</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">        </span>    if descr is not None:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">   </span>        f = descr.__get__</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">         </span>if f is not None and descr.__set__ is not None:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">               </span>    # Data descriptor</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">               </span>    return f(descr, self, type(self))</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">            dict = self.__dict__</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">      </span>    if dict is not None:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">    </span>        try:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">              </span>    return self.__dict__[name]</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">                except KeyError:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">        </span>            pass</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">            if f is not None:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">  </span>        # Non-data descriptor</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">     </span>        return f(descr, self, type(self))</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">            if descr is not None:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">       </span>        # Regular class attribute</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre"> </span>        return descr</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">            raise AttributeError(name)</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">    class super:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">        def __getattribute__(self, name):</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">  </span>   assert isinstance(name, unicode)</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre"> </span>   if name != '__class__':</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">       </span>       starttype = self.__self_type__</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">  </span>       mro = startype.mro()</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">       </span>       try:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">    </span>           idx = mro.index(self.__thisclass__)</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">  </span>       except ValueError:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">      </span>           pass</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre"> </span>       else:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">   </span>           for base in mro[idx+1:]:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">          </span>       # PEP 447 will change these lines:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">              </span>       try:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">            </span>           descr = base.__dict__[name]</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">                       except KeyError:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">              </span>           continue</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">             </span>       f = descr.__get__</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">               </span>       if f is not None:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">               </span>           return f(descr,</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">                   </span>       None if (self.__self__ is self.__self_type__) else self.__self__,</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">                       </span>       starttype)</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">         </span>       else:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">           </span>           return descr</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre"> </span>   return object.__getattribute__(self, name)</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">This PEP should change the dict lookup at the lines starting at "# PEP 447" with</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">a method call to perform the actual lookup, making is possible to affect that</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">lookup both for normal attribute access and access through the `super proxy`_.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Note that specific classes can already completely override the default</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">behaviour by implementing their own ``__getattribute__`` slot (with or without</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">calling the super class implementation).</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">In Python code</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">--------------</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">A meta type can define a method ``__getdescriptor__`` that is called during</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">attribute resolution by both ``super.__getattribute__``</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">and ``object.__getattribute``::</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">    class MetaType(type):</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">        def __getdescriptor__(cls, name):</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">            try:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">                return cls.__dict__[name]</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">            except KeyError:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">                raise AttributeError(name) from None</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The ``__getdescriptor__`` method has as its arguments a class (which is an</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">instance of the meta type) and the name of the attribute that is looked up.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">It should return the value of the attribute without invoking descriptors,</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">and should raise `AttributeError`_ when the name cannot be found.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The `type`_ class provides a default implementation for ``__getdescriptor__``,</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">that looks up the name in the class dictionary.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Example usage</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.............</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The code below implements a silly metaclass that redirects attribute lookup to</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">uppercase versions of names::</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">    class UpperCaseAccess (type):</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">        def __getdescriptor__(cls, name):</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">      </span>    try:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">                return cls.__dict__[name.upper()]</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">     </span>    except KeyError:</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><span class="Apple-tab-span" style="white-space:pre">        </span>        raise AttributeError(name) from None</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">    class SillyObject (metaclass=UpperCaseAccess):</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">        def m(self):</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">            return 42</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">        def M(self):</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">            return "fourtytwo"</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">    obj = SillyObject()</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">    assert obj.m() == "fortytwo"</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">As mentioned earlier in this PEP a more realistic use case of this</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">functionallity is a ``__getdescriptor__`` method that dynamicly populates the</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">class ``__dict__`` based on attribute access, primarily when it is not</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">possible to reliably keep the class dict in sync with its source, for example</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">because the source used to populate ``__dict__`` is dynamic as well and does</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">not have triggers that can be used to detect changes to that source.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">An example of that are the class bridges in PyObjC: the class bridge is a</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Python object (class) that represents an Objective-C class and conceptually</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">has a Python method for every Objective-C method in the Objective-C class.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">As with Python it is possible to add new methods to an Objective-C class, or</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">replace existing ones, and there are no callbacks that can be used to detect</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">this.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">In C code</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">---------</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">A new slot ``tp_getdescriptor`` is added to the ``PyTypeObject`` struct, this</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">slot corresponds to the ``__getdescriptor__`` method on `type`_.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The slot has the following prototype::</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">    PyObject* (*getdescriptorfunc)(PyTypeObject* cls, PyObject* name);</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">This method should lookup *name* in the namespace of *cls*, without looking at</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">superclasses, and should not invoke descriptors. The method returns ``NULL``</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">without setting an exception when the *name* cannot be found, and returns a</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">new reference otherwise (not a borrowed reference).</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Use of this hook by the interpreter</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">-----------------------------------</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The new method is required for metatypes and as such is defined on `type_`.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Both ``super.__getattribute__`` and</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">``object.__getattribute__``/`PyObject_GenericGetAttr`_</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">(through ``_PyType_Lookup``) use the this ``__getdescriptor__`` method when</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">walking the MRO.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Other changes to the implementation</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">-----------------------------------</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The change for `PyObject_GenericGetAttr`_ will be done by changing the private</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">function ``_PyType_Lookup``. This currently returns a borrowed reference, but</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">must return a new reference when the ``__getdescriptor__`` method is present.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Because of this ``_PyType_Lookup`` will be renamed to ``_PyType_LookupName``,</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">this will cause compile-time errors for all out-of-tree users of this</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">private API.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The attribute lookup cache in ``Objects/typeobject.c`` is disabled for classes</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">that have a metaclass that overrides ``__getdescriptor__``, because using the</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">cache might not be valid for such classes.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Impact of this PEP on introspection</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">-----------------------------------</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Use of the method introduced in this PEP can affect introspection of classes</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">with a metaclass that uses a custom ``__getdescriptor__`` method. This section</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">lists those changes.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The items listed below are only affected by custom ``__getdescriptor__``</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">methods, the default implementation for ``object`` won't cause problems</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">because that still only uses the class ``__dict__`` and won't cause visible</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">changes to the visible behaviour of the ``object.__getattribute__``.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">* ``dir`` might not show all attributes</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  As with a custom ``__getattribute__`` method `dir()`_ might not see all</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  (instance) attributes when using the ``__getdescriptor__()`` method to</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  dynamicly resolve attributes.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  The solution for that is quite simple: classes using ``__getdescriptor__``</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  should also implement `__dir__()`_ if they want full support for the builtin</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  `dir()`_ function.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">* ``inspect.getattr_static`` might not show all attributes</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  The function ``inspect.getattr_static`` intentionally does not invoke</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  ``__getattribute__`` and descriptors to avoid invoking user code during</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  introspection with this function. The ``__getdescriptor__`` method will also</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  be ignored and is another way in which the result of ``inspect.getattr_static``</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  can be different from that of ``builtin.getattr``.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">* ``inspect.getmembers`` and ``inspect.get_class_attrs``</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  Both of these functions directly access the class __dict__ of classes along</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  the MRO, and hence can be affected by a custom ``__getdescriptor__`` method.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  **TODO**: I haven't fully worked out what the impact of this is, and if there</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  are mitigations for those using either updates to these functions, or</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  additional methods that users should implement to be fully compatible with</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  these functions.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  One possible mitigation is to have a custom ``__getattribute__`` for these</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  classes that fills ``__dict__`` before returning and and defers to the</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  default implementation for other attributes.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">* Direct introspection of the class ``__dict__``</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  Any code that directly access the class ``__dict__`` for introspection</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">  can be affected by a custom ``__getdescriptor__`` method.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Performance impact</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">------------------</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">**WARNING**: The benchmark results in this section are old, and will be updated</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">when I've ported the patch to the current trunk. I don't expect significant</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">changes to the results in this section.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class="">[snipped]</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Alternative proposals</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">---------------------</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">``__getattribute_super__``</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">..........................</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">An earlier version of this PEP used the following static method on classes::</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">    def __getattribute_super__(cls, name, object, owner): pass</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">This method performed name lookup as well as invoking descriptors and was</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">necessarily limited to working only with ``super.__getattribute__``.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Reuse ``tp_getattro``</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.....................</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">It would be nice to avoid adding a new slot, thus keeping the API simpler and</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">easier to understand.  A comment on `Issue 18181`_ asked about reusing the</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">``tp_getattro`` slot, that is super could call the ``tp_getattro`` slot of all</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">methods along the MRO.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">That won't work because ``tp_getattro`` will look in the instance</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">``__dict__`` before it tries to resolve attributes using classes in the MRO.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">This would mean that using ``tp_getattro`` instead of peeking the class</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">dictionaries changes the semantics of the `super class`_.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Alternate placement of the new method</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.....................................</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">This PEP proposes to add ``__getdescriptor__`` as a method on the metaclass.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">An alternative would be to add it as a class method on the class itself</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">(simular to how ``__new__`` is a `staticmethod`_ of the class and not a method</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">of the metaclass).</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">The two are functionally equivalent, and there's something to be said about</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">not requiring the use of a meta class.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">References</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">==========</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">* `Issue 18181`_ contains an out of date prototype implementation</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">Copyright</div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">=========</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">This document has been placed in the public domain.</div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.. _`Issue 18181`: <a href="http://bugs.python.org/issue18181" class="">http://bugs.python.org/issue18181</a></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.. _`super class`: <a href="http://docs.python.org/3/library/functions.html#super" class="">http://docs.python.org/3/library/functions.html#super</a></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.. _`super proxy`: <a href="http://docs.python.org/3/library/functions.html#super" class="">http://docs.python.org/3/library/functions.html#super</a></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.. _`super`: <a href="http://docs.python.org/3/library/functions.html#super" class="">http://docs.python.org/3/library/functions.html#super</a></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.. _`dir()`: <a href="http://docs.python.org/3/library/functions.html#dir" class="">http://docs.python.org/3/library/functions.html#dir</a></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.. _`staticmethod`: <a href="http://docs.python.org/3/library/functions.html#staticmethod" class="">http://docs.python.org/3/library/functions.html#staticmethod</a></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.. _`__dir__()`: <a href="https://docs.python.org/3/reference/datamodel.html#object.__dir__" class="">https://docs.python.org/3/reference/datamodel.html#object.__dir__</a></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.. _`NotImplemented`: <a href="http://docs.python.org/3/library/constants.html#NotImplemented" class="">http://docs.python.org/3/library/constants.html#NotImplemented</a></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.. _`PyObject_GenericGetAttr`: <a href="http://docs.python.org/3/c-api/object.html#PyObject_GenericGetAttr" class="">http://docs.python.org/3/c-api/object.html#PyObject_GenericGetAttr</a></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.. _`type`: <a href="http://docs.python.org/3/library/functions.html#type" class="">http://docs.python.org/3/library/functions.html#type</a></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.. _`AttributeError`: <a href="http://docs.python.org/3/library/exceptions.html#AttributeError" class="">http://docs.python.org/3/library/exceptions.html#AttributeError</a></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.. _`PyObjC`: <a href="http://pyobjc.sourceforge.net/" class="">http://pyobjc.sourceforge.net/</a></div><div style="margin: 0px; font-size: 11px; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; font-family: Menlo;" class="">.. _`classmethod`: <a href="http://docs.python.org/3/library/functions.html#classmethod" class="">http://docs.python.org/3/library/functions.html#classmethod</a></div></div></body></html>