diff --git a/pep-0000.txt b/pep-0000.txt index 15e397aad..048b288c6 100644 --- a/pep-0000.txt +++ b/pep-0000.txt @@ -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 diff --git a/pep-0367.txt b/pep-0367.txt new file mode 100644 index 000000000..e3bfb2943 --- /dev/null +++ b/pep-0367.txt @@ -0,0 +1,360 @@ +PEP: 367 +Title: New Super +Version: $Revision$ +Last-Modified: $Date$ +Author: Calvin Spealman +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: