Version of my super getattr hook proposal as posted to python-dev.
This will likely see significant changes based on feedback from Steve Dower.
This commit is contained in:
parent
10b82df874
commit
e1f31edcf2
|
@ -0,0 +1,161 @@
|
||||||
|
PEP: 447
|
||||||
|
Title: Hooking into super attribute resolution
|
||||||
|
Version: $Revision$
|
||||||
|
Last-Modified: $Date$
|
||||||
|
Author: Ronald Oussoren <ronaldoussoren@mac.com>
|
||||||
|
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
|
||||||
|
|
Loading…
Reference in New Issue