361 lines
12 KiB
Plaintext
361 lines
12 KiB
Plaintext
|
PEP: 367
|
|||
|
Title: New Super
|
|||
|
Version: $Revision$
|
|||
|
Last-Modified: $Date$
|
|||
|
Author: Calvin Spealman <ironfroggy@gmail.com>
|
|||
|
Status: Draft
|
|||
|
Type: Standards Track
|
|||
|
Content-Type: text/x-rst
|
|||
|
Created: 28-Apr-2007
|
|||
|
Python-Version: 2.6
|
|||
|
Post-History: 28-Apr-2007, 29-Apr-2007 (1), 29-Apr-2007 (2)
|
|||
|
|
|||
|
|
|||
|
Abstract
|
|||
|
========
|
|||
|
|
|||
|
The PEP defines the proposal to enhance the ``super`` builtin to work
|
|||
|
implicitly upon the class within which it is used and upon the
|
|||
|
instance the current function was called on. The premise of the new
|
|||
|
super usage suggested is as follows::
|
|||
|
|
|||
|
super.foo(1, 2)
|
|||
|
|
|||
|
to replace the old ::
|
|||
|
|
|||
|
super(Foo, self).foo(1, 2)
|
|||
|
|
|||
|
|
|||
|
Rationale
|
|||
|
=========
|
|||
|
|
|||
|
The current usage of ``super`` requires an explicit passing of both
|
|||
|
the class and instance it must operate from, requiring a breaking of
|
|||
|
the *DRY* (Don't Repeat Yourself) rule. This hinders any change in
|
|||
|
class name, and is often considered a wart by many.
|
|||
|
|
|||
|
|
|||
|
Specification
|
|||
|
=============
|
|||
|
|
|||
|
Within the specification section, some special terminology will be
|
|||
|
used to distinguish similar and closely related concepts. "Super
|
|||
|
type" will refer to the actual builtin type named ``super``. "Next
|
|||
|
Class/Type in the MRO" will refer to the class where attribute lookups
|
|||
|
will be performed by ``super``, for example, in the following, ``A``
|
|||
|
is the "Next class in the MRO" for the use of ``super``. ::
|
|||
|
|
|||
|
class A(object):
|
|||
|
def f(self):
|
|||
|
return 'A'
|
|||
|
|
|||
|
class B(A):
|
|||
|
def f(self):
|
|||
|
super(B, self).f() # Here, A would be our "Next class
|
|||
|
# in the MRO", of course.
|
|||
|
|
|||
|
A "super object" is simply an instance of the super type, which is
|
|||
|
associated with a class and possibly with an instance of that class.
|
|||
|
Finally, "new super" refers to the new super type, which will replace
|
|||
|
the original.
|
|||
|
|
|||
|
Replacing the old usage of ``super``, calls to the next class in the
|
|||
|
MRO (method resolution order) will be made without an explicit super
|
|||
|
object creation, by simply accessing an attribute on the super type
|
|||
|
directly, which will automatically apply the class and instance to
|
|||
|
perform the proper lookup. The following example demonstrates the use
|
|||
|
of this. ::
|
|||
|
|
|||
|
class A(object):
|
|||
|
def f(self):
|
|||
|
return 'A'
|
|||
|
|
|||
|
class B(A):
|
|||
|
def f(self):
|
|||
|
return 'B' + super.f()
|
|||
|
|
|||
|
class C(A):
|
|||
|
def f(self):
|
|||
|
return 'C' + super.f()
|
|||
|
|
|||
|
class D(B, C):
|
|||
|
def f(self):
|
|||
|
return 'D' + super.f()
|
|||
|
|
|||
|
assert D().f() == 'DBCA'
|
|||
|
|
|||
|
The proposal adds a dynamic attribute lookup to the super type, which
|
|||
|
will automatically determine the proper class and instance parameters.
|
|||
|
Each super attribute lookup identifies these parameters and performs
|
|||
|
the super lookup on the instance, as the current super implementation
|
|||
|
does with the explicit invocation of a super object upon a class and
|
|||
|
instance.
|
|||
|
|
|||
|
The enhancements to the super type will define a new ``__getattr__``
|
|||
|
classmethod of the super type, which must look backwards to the
|
|||
|
previous frame and locate the instance object. This can be naively
|
|||
|
determined by located the local named by the first argument to the
|
|||
|
function. Using super outside of a function where this is a valid
|
|||
|
lookup for the instance can be considered undocumented in its
|
|||
|
behavior. This special method will actually be invoked on attribute
|
|||
|
lookups to the super type itself, as opposed to super objects, as the
|
|||
|
current implementation works. This may pose open issues, which are
|
|||
|
detailed below.
|
|||
|
|
|||
|
"Every class will gain a new special attribute, ``__super__``, which
|
|||
|
refers to an instance of the associated super object for that class."
|
|||
|
In this capacity, the new super also acts as its own descriptor,
|
|||
|
create an instance-specific super upon lookup.
|
|||
|
|
|||
|
Much of this was discussed in the thread of the python-dev list,
|
|||
|
"Fixing super anyone?" [1]_.
|
|||
|
|
|||
|
Open Issues
|
|||
|
-----------
|
|||
|
|
|||
|
__call__ methods
|
|||
|
''''''''''''''''
|
|||
|
|
|||
|
Backward compatibility of the super type API raises some issues.
|
|||
|
Names, the lookup of the ``__call__`` method of the super type itself,
|
|||
|
which means a conflict with doing an actual super lookup of the
|
|||
|
``__call__`` attribute. Namely, the following is ambiguous in the
|
|||
|
current proposal::
|
|||
|
|
|||
|
super.__call__(arg)
|
|||
|
|
|||
|
Which means the backward compatible API, which involves instantiating
|
|||
|
the super type, will either not be possible, because it will actually
|
|||
|
do a super lookup on the ``__call__`` attribute, or there will be no
|
|||
|
way to perform a super lookup on the ``__call__`` attribute. Both
|
|||
|
seem unacceptable, so any suggestions are welcome.
|
|||
|
|
|||
|
Actually keeping the old super around in 2.x and creating a completely
|
|||
|
new super type separately may be the best option. A future import or
|
|||
|
even a simple import in 2.x of the new super type from some built-in
|
|||
|
module may offer a way to choose which each module uses, even mixing
|
|||
|
uses by binding to different names. Such a built-in module might be
|
|||
|
called 'newsuper'. This module is also the reference implementation,
|
|||
|
which I will present below.
|
|||
|
|
|||
|
super type's new getattr
|
|||
|
''''''''''''''''''''''''
|
|||
|
|
|||
|
To give the behavior needed, the super type either needs a way to do
|
|||
|
dynamic lookup of attributes on the super type object itself or define
|
|||
|
a metaclass for the built-in type. This author is unsure which, if
|
|||
|
either, is possible with C-defined types.
|
|||
|
|
|||
|
When should we create __super__ attributes?
|
|||
|
'''''''''''''''''''''''''''''''''''''''''''
|
|||
|
|
|||
|
They either need to be created on class creation or on ``__super__``
|
|||
|
attribute lookup. For the second, they could be cached, of course,
|
|||
|
which seems like it may be the best idea, if implicit creation of a
|
|||
|
super object for every class is considered too much overhead.
|
|||
|
|
|||
|
How does it work in inner functions?
|
|||
|
''''''''''''''''''''''''''''''''''''
|
|||
|
|
|||
|
If a method defines a function and super is used inside of it, how
|
|||
|
does this work? The frame looking and instance detection breaks here.
|
|||
|
However, if there can be some unambiguous way to use both the new
|
|||
|
super form and still be able to explicitly name the type and instance,
|
|||
|
I think its an acceptable tradeoff to simply be explicit in these
|
|||
|
cases, rather than add weird super-specific lookup rules in these
|
|||
|
cases.
|
|||
|
|
|||
|
An example of such a problematic bit of code is::
|
|||
|
|
|||
|
class B(A):
|
|||
|
def f(self):
|
|||
|
def g():
|
|||
|
return super.f()
|
|||
|
return g()
|
|||
|
|
|||
|
Should super actually become a keyword?
|
|||
|
'''''''''''''''''''''''''''''''''''''''
|
|||
|
|
|||
|
This would solve many of the problems and allow more direct
|
|||
|
implementation of super into the language proper. However, some are
|
|||
|
against the actual keywordization of super. The simplest solution is
|
|||
|
often the correct solution and the simplest solution may well not be
|
|||
|
adding additional keywords to the language when they are not needed.
|
|||
|
Still, it may solve many of the other open issues.
|
|||
|
|
|||
|
Can we also allow super()?
|
|||
|
''''''''''''''''''''''''''
|
|||
|
|
|||
|
There is strong sentiment for and against this, but implementation and
|
|||
|
style concerns are obvious. Particularly, that it's "magical" and
|
|||
|
that ``super()`` would differ from ``super.__call__()``, being very
|
|||
|
unpythonic.
|
|||
|
|
|||
|
|
|||
|
Reference Implementation
|
|||
|
========================
|
|||
|
|
|||
|
This implementation was a cooperative contribution in the original thread [1]_. ::
|
|||
|
|
|||
|
#!/usr/bin/env python
|
|||
|
#
|
|||
|
# newsuper.py
|
|||
|
|
|||
|
import sys
|
|||
|
|
|||
|
class SuperMetaclass(type):
|
|||
|
def __getattr__(cls, attr):
|
|||
|
calling_frame = sys._getframe().f_back
|
|||
|
instance_name = calling_frame.f_code.co_varnames[0]
|
|||
|
instance = calling_frame.f_locals[instance_name]
|
|||
|
return getattr(instance.__super__, attr)
|
|||
|
|
|||
|
class Super(object):
|
|||
|
__metaclass__ = SuperMetaclass
|
|||
|
def __init__(self, type, obj=None):
|
|||
|
if isinstance(obj, Super):
|
|||
|
obj = obj.__obj__
|
|||
|
self.__type__ = type
|
|||
|
self.__obj__ = obj
|
|||
|
def __get__(self, obj, cls=None):
|
|||
|
if obj is None:
|
|||
|
raise Exception('only supports instances')
|
|||
|
else:
|
|||
|
return Super(self.__type__, obj)
|
|||
|
def __getattr__(self, attr):
|
|||
|
mro = iter(self.__obj__.__class__.__mro__)
|
|||
|
for cls in mro:
|
|||
|
if cls is self.__type__:
|
|||
|
break
|
|||
|
for cls in mro:
|
|||
|
if attr in cls.__dict__:
|
|||
|
x = cls.__dict__[attr]
|
|||
|
if hasattr(x, '__get__'):
|
|||
|
x = x.__get__(self, cls)
|
|||
|
return x
|
|||
|
raise AttributeError, attr
|
|||
|
|
|||
|
class autosuper(type):
|
|||
|
def __init__(cls, name, bases, clsdict):
|
|||
|
cls.__super__ = Super(cls)
|
|||
|
|
|||
|
if __name__ == '__main__':
|
|||
|
class A(object):
|
|||
|
__metaclass__ = autosuper
|
|||
|
def f(self):
|
|||
|
return 'A'
|
|||
|
|
|||
|
class B(A):
|
|||
|
def f(self):
|
|||
|
return 'B' + Super.f()
|
|||
|
|
|||
|
class C(A):
|
|||
|
def f(self):
|
|||
|
return 'C' + Super.f()
|
|||
|
|
|||
|
class D(B, C):
|
|||
|
def f(self, arg=None):
|
|||
|
var = None
|
|||
|
return 'D' + Super.f()
|
|||
|
|
|||
|
assert D().f() == 'DBCA'
|
|||
|
|
|||
|
|
|||
|
Alternative Proposals
|
|||
|
=====================
|
|||
|
|
|||
|
No Changes
|
|||
|
----------
|
|||
|
|
|||
|
Although its always attractive to just keep things how they are,
|
|||
|
people have sought a change in the usage of super calling for some
|
|||
|
time, and for good reason, all mentioned previously.
|
|||
|
|
|||
|
* Decoupling from the class name (which might not even be bound to the
|
|||
|
right class anymore!).
|
|||
|
|
|||
|
* Simpler looking, cleaner super calls would be better.
|
|||
|
|
|||
|
``super(__this_class__, self)``
|
|||
|
-------------------------------
|
|||
|
|
|||
|
This is nearly an anti-proposal, as it basically relies on the
|
|||
|
acceptance of the ``__this_class__`` PEP [#pep3130]_, which proposes a
|
|||
|
special name that would always be bound to the class within which it
|
|||
|
is used. If that is accepted, ``__this_class__`` could simply be used
|
|||
|
instead of the class' name explicitly, solving the name binding issues.
|
|||
|
|
|||
|
``self.__super__.foo(*args)``
|
|||
|
-----------------------------
|
|||
|
|
|||
|
The ``__super__`` attribute is mentioned in this PEP in several
|
|||
|
places, and could be a candidate for the complete solution, actually
|
|||
|
using it explicitly instead of any super usage directly. However,
|
|||
|
double-underscore names are usually an internal detail, and attempted
|
|||
|
to be kept out of everyday code.
|
|||
|
|
|||
|
``super(self, *args) or __super__(self, *args)``
|
|||
|
------------------------------------------------
|
|||
|
|
|||
|
This solution only solves the problem of the type indication, does not
|
|||
|
handle differently named super methods, and is explicit about the name
|
|||
|
of the instance. It is less flexible without being able to enacted on
|
|||
|
other method names, in cases where that is needed. One use case where
|
|||
|
this fails is when a base class has a factory classmethod and a
|
|||
|
subclass has two factory classmethods, both of which need to properly
|
|||
|
make super calls to the one in the base class.
|
|||
|
|
|||
|
``super.foo(self, *args)``
|
|||
|
--------------------------
|
|||
|
|
|||
|
This variation actually eliminates the problems with locating the
|
|||
|
proper instance, and if any of the alternatives were pushed into the
|
|||
|
spotlight, I would want it to be this one.
|
|||
|
|
|||
|
``super`` or ``super()``
|
|||
|
------------------------
|
|||
|
|
|||
|
This proposal leaves no room for different names, signatures, or
|
|||
|
application to other classes, or instances. A way to allow some
|
|||
|
similar use alongside the normal proposal would be favorable,
|
|||
|
encouraging good design of multiple inheritance trees and compatible
|
|||
|
methods.
|
|||
|
|
|||
|
|
|||
|
History
|
|||
|
=======
|
|||
|
|
|||
|
29-Apr-2007:
|
|||
|
|
|||
|
- Changed title from "Super As A Keyword" to "New Super"
|
|||
|
- Updated much of the language and added a terminology section
|
|||
|
for clarification in confusing places.
|
|||
|
- Added reference implementation and history sections.
|
|||
|
|
|||
|
|
|||
|
References
|
|||
|
==========
|
|||
|
|
|||
|
.. [1] Fixing super anyone?
|
|||
|
(http://mail.python.org/pipermail/python-3000/2007-April/006667.html)
|
|||
|
|
|||
|
.. [#pep3130] PEP 3130 (Access to Current Module/Class/Function)
|
|||
|
http://www.python.org/dev/peps/pep-3130
|
|||
|
|
|||
|
|
|||
|
Copyright
|
|||
|
=========
|
|||
|
|
|||
|
This document has been placed in the public domain.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
..
|
|||
|
Local Variables:
|
|||
|
mode: indented-text
|
|||
|
indent-tabs-mode: nil
|
|||
|
sentence-end-double-space: t
|
|||
|
fill-column: 70
|
|||
|
coding: utf-8
|
|||
|
End:
|