Add PEP 367, "New Super" by Calvin Spealman.
This commit is contained in:
parent
04564c4ff7
commit
d1b27f72bf
|
@ -112,6 +112,7 @@ Index by Category
|
|||
S 364 Transitioning to the Py3K Standard Library Warsaw
|
||||
S 365 Adding the pkg_resources module Eby
|
||||
S 366 Main module explicit relative imports Coghlan
|
||||
S 367 New Super Spealman
|
||||
S 754 IEEE 754 Floating Point Special Values Warnes
|
||||
S 3101 Advanced String Formatting Talin
|
||||
S 3108 Standard Library Reorganization Cannon
|
||||
|
@ -464,6 +465,7 @@ Numerical Index
|
|||
S 364 Transitioning to the Py3K Standard Library Warsaw
|
||||
S 365 Adding the pkg_resources module Eby
|
||||
S 366 Main module explicit relative imports Coghlan
|
||||
S 367 New Super Spealman
|
||||
SR 666 Reject Foolish Indentation Creighton
|
||||
S 754 IEEE 754 Floating Point Special Values Warnes
|
||||
P 3000 Python 3000 GvR
|
||||
|
@ -606,6 +608,7 @@ Owners
|
|||
Schneider-Kamp, Peter nowonder@nowonder.de
|
||||
Seo, Jiwon seojiwon@gmail.com
|
||||
Smith, Kevin D. Kevin.Smith@theMorgue.org
|
||||
Spealman, Calvin ironfroggy@gmail.com
|
||||
Stein, Greg gstein@lyra.org
|
||||
Stutzbach, Daniel daniel.stutzbach@gmail.com
|
||||
Suzi, Roman rnd@onego.ru
|
||||
|
|
|
@ -0,0 +1,360 @@
|
|||
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:
|
Loading…
Reference in New Issue