PEP: TODO Title: Hooking into super attribute resolution Version: $Revision$ Last-Modified: $Date$ Author: Ronald Oussoren Status: Draft Type: Standards Track Content-Type: text/x-rst Created: 12-Jun-2013 Post-History: 2-Jul-2013, ? Abstract ======== In current python releases the attribute resolution of the `super class`_ peeks in the ``__dict__`` attribute of classes on the MRO to look for attributes. This PEP introduces a hook that classes can use to override that behavior for specific classes. Rationale ========= Peeking in the class ``__dict__`` works for regular classes, but can cause problems when a class dynamicly looks up attributes in a ``__getattribute__`` method. The new hook makes it possible to introduce the same customization for attribute lookup through the `super class`_. The superclass attribute lookup hook ==================================== In C code --------- A new slot ``tp_getattro_super`` is added to the ``PyTypeObject`` struct. The ``tp_getattro`` slot for super will call this slot when it is not ``NULL``, and will raise an exception when it is not set (which shouldn't happen because the method is implemented for :class:`object`). The slot has the following prototype:: PyObject* (*getattrosuperfunc)(PyTypeObject* cls, PyObject* name, PyObject* object, PyObject* owner); The function should perform attribute lookup on *object* for *name*, but only looking in type *tp* (which will be one of the types on the MRO for *self*) and without looking in the instance *__dict__*. The function returns ``NULL`` when the attribute cannot be found, and raises and exception. Exception other than ``AttributeError`` will cause failure of super's attribute resolution. The implementation of the slot for the :class:`object` type is ``PyObject_GenericGetAttrSuper``, which peeks in the ``tp_dict`` for *cls*. Note that *owner* and *object* will be the same object when using a class-mode super. In Python code -------------- A Python class can contain a definition for a static method ``__getattribute_super__`` with the following prototype:: def __getattribute_super__(cls, name, object, owner): pass The method should perform attribute lookup for *name* on instance *self* while only looking at *cls* (it should not look in super classes or the instance *__dict__* XXX: I haven't got a clue at the moment if the method should be an instance-, class- or staticmethod. The prototype uses a staticmethod. XXX: My prototype automagicly makes this a static method, just like __new__ is made into a static method. That's more convenient, but also (too?) magical. XXX: Should this raise AttributeError or return a magic value to signal that an attribute cannot be found (such as NotImplemented, used in the comparison operators)? I'm currently using an exception, a magical return value would be slightly more efficient because the exception machinery is not invoked. Alternative proposals --------------------- Reuse ``tp_getattro`` ..................... It would be nice to avoid adding a new slot, thus keeping the API simpler and easier to understand. A comment on `Issue 18181`_ asked about reusing the ``tp_getattro`` slot, that is super could call the ``tp_getattro`` slot of all methods along the MRO. AFAIK that won't work because ``tp_getattro`` will look in the instance ``__dict__`` before it tries to resolve attributes using classes in the MRO. This would mean that using ``tp_getattro`` instead of peeking the class dictionaries changes the semantics of the `super class`_. Open Issues =========== * The names of the new slot and magic method are far from settled. * I'm not too happy with the prototype for the new hook. * Should ``__getattribute_super__`` be a class method instead? -> Yes? The method looks up a named attribute name of an object in a specific class. Is also likely needed to deal with @classmethod and super(Class, Class) * Should ``__getattribute_super__`` be defined on object? -> Yes: makes it easier to delegate to the default implementation * This doesn't necessarily work for class method super class (e.g. super(object, object))... References ========== * `Issue 18181`_ contains a prototype implementation The prototype uses different names than this proposal. Copyright ========= This document has been placed in the public domain. .. _`Issue 18181`: http://bugs.python.org/issue18181 .. _`super class`: http://docs.python.org/3/library/functions.html?highlight=super#super Changes ======= * 3-jul-2013: + added note question about having object.__getattribute_super__ + added note about class super (super(Cls, Cls).classmethod) + changed to API for the python and C functions: - argument order - now a class method - added 'owner' argument (same as object.__get__) + added PyObject_GenericGetAttroSuper