[Python-checkins] r55061 - peps/trunk/pep-0000.txt peps/trunk/pep-0367.txt

georg.brandl python-checkins at python.org
Tue May 1 23:11:55 CEST 2007


Author: georg.brandl
Date: Tue May  1 23:11:53 2007
New Revision: 55061

Added:
   peps/trunk/pep-0367.txt   (contents, props changed)
Modified:
   peps/trunk/pep-0000.txt
Log:
Add PEP 367, "New Super" by Calvin Spealman.


Modified: peps/trunk/pep-0000.txt
==============================================================================
--- peps/trunk/pep-0000.txt	(original)
+++ peps/trunk/pep-0000.txt	Tue May  1 23:11:53 2007
@@ -112,6 +112,7 @@
  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 @@
  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 @@
     Schneider-Kamp, Peter    nowonder at nowonder.de
     Seo, Jiwon               seojiwon at gmail.com
     Smith, Kevin D.          Kevin.Smith at theMorgue.org
+    Spealman, Calvin         ironfroggy at gmail.com
     Stein, Greg              gstein at lyra.org
     Stutzbach, Daniel        daniel.stutzbach at gmail.com
     Suzi, Roman              rnd at onego.ru

Added: peps/trunk/pep-0367.txt
==============================================================================
--- (empty file)
+++ peps/trunk/pep-0367.txt	Tue May  1 23:11:53 2007
@@ -0,0 +1,360 @@
+PEP: 367
+Title: New Super
+Version: $Revision$
+Last-Modified: $Date$
+Author: Calvin Spealman <ironfroggy at 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:


More information about the Python-checkins mailing list