Add PEP 367, "New Super" by Calvin Spealman.

This commit is contained in:
Georg Brandl 2007-05-01 21:11:53 +00:00
parent 04564c4ff7
commit d1b27f72bf
2 changed files with 363 additions and 0 deletions

View File

@ -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

360
pep-0367.txt Normal file
View File

@ -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: