[Python-checkins] r70312 - peps/trunk/pep-3135.txt

guido.van.rossum python-checkins at python.org
Wed Mar 11 20:14:30 CET 2009


Author: guido.van.rossum
Date: Wed Mar 11 20:14:30 2009
New Revision: 70312

Log:
Finally update the super() PEP.  Lie Ryan submitted a draft which I updated
a bit more.  I think this is good enough now.


Modified:
   peps/trunk/pep-3135.txt

Modified: peps/trunk/pep-3135.txt
==============================================================================
--- peps/trunk/pep-3135.txt	(original)
+++ peps/trunk/pep-3135.txt	Wed Mar 11 20:14:30 2009
@@ -3,13 +3,14 @@
 Version: $Revision$
 Last-Modified: $Date$
 Author: Calvin Spealman <ironfroggy at gmail.com>,
-        Tim Delaney <timothy.c.delaney at gmail.com>
-Status: Draft
+        Tim Delaney <timothy.c.delaney at gmail.com>,
+        Lie Ryan <lie.1296 at gmail.com>
+Status: Accepted
 Type: Standards Track
 Content-Type: text/x-rst
 Created: 28-Apr-2007
 Python-Version: 3.0
-Post-History: 28-Apr-2007, 29-Apr-2007 (1), 29-Apr-2007 (2), 14-May-2007
+Post-History: 28-Apr-2007, 29-Apr-2007 (1), 29-Apr-2007 (2), 14-May-2007, 12-Mar-2009
 
 Numbering Note
 ==============
@@ -17,11 +18,6 @@
 This PEP started its life as PEP 367.  Since it is now targeted
 for Python 3000, it has been moved into the 3xxx space.
 
-NOTE: This PEP needs to be rewritten to match reality.  The actual
-implementation is quite different than what is described here;
-effectively, super() equals to super(C, self) where C is the current
-class and self is the first argument of the current function.
-
 Abstract
 ========
 
@@ -32,19 +28,12 @@
 
 The premise of the new super usage suggested is as follows::
 
-    super.foo(1, 2)
+    super().foo(1, 2)
 
 to replace the old::
 
     super(Foo, self).foo(1, 2)
 
-and the current ``__builtin__.super`` be aliased to ``__builtin__.__super__``
-(with ``__builtin__.super`` to be removed in Python 3.0).
-
-It is further proposed that assignment to ``super`` become a ``SyntaxError``,
-similar to the behaviour of ``None``.
-
-
 Rationale
 =========
 
@@ -58,83 +47,54 @@
 =============
 
 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". A "super instance" is simply an instance
-of the super type, which is associated with a class and possibly with an
-instance of that class.
-
-Because the new ``super`` semantics are not backwards compatible with Python
-2.5, the new semantics will require a ``__future__`` import::
-
-    from __future__ import new_super
-
-The current ``__builtin__.super`` will be aliased to ``__builtin__.__super__``.
-This will occur regardless of whether the new ``super`` semantics are active.
-It is not possible to simply rename ``__builtin__.super``, as that would affect
-modules that do not use the new ``super`` semantics. In Python 3.0 it is
-proposed that the name ``__builtin__.super`` will be removed.
-
-Replacing the old usage of super, calls to the next class in the MRO (method
-resolution order) can be made without explicitly creating a ``super``
-instance (although doing so will still be supported via ``__super__``). Every
-function will have an implicit local named ``super``. This name behaves
-identically to a normal local, including use by inner functions via a cell,
-with the following exceptions:
+distinguish similar and closely related concepts. "super class" will refer to
+the actual builtin class named "super". A "super instance" is simply an
+instance of the super class, which is associated with another class and
+possibly with an instance of that class.
 
-1. Assigning to the name ``super`` will raise a ``SyntaxError`` at compile time;
+The new ``super`` semantics are only available in Python 3.0.
 
-2. Calling a static method or normal function that accesses the name ``super``
-   will raise a ``TypeError`` at runtime.
-
-Every function that uses the name ``super``, or has an inner function that
-uses the name ``super``, will include a preamble that performs the equivalent
-of::
+Replacing the old usage of super, calls to the next class in the MRO (method
+resolution order) can be made without explicitly passing the class object
+(although doing so will still be supported). Every function
+will have a cell named ``__class__`` that contains the class object that the
+function is defined in.
 
-    super = __builtin__.__super__(<class>, <instance>)
+The new syntax::
 
-where ``<class>`` is the class that the method was defined in, and
-``<instance>`` is the first parameter of the method (normally ``self`` for
-instance methods, and ``cls`` for class methods). For static methods and normal
-functions, ``<class>`` will be ``None``, resulting in a ``TypeError`` being
-raised during the preamble.
+    super()
 
-Note: The relationship between ``super`` and ``__super__`` is similar to that
-between ``import`` and ``__import__``.
+is equivalent to::
 
-Much of this was discussed in the thread of the python-dev list, "Fixing super
-anyone?" [1]_.
+    super(__class__, <firstarg>)
 
+where ``__class__`` is the class that the method was defined in, and
+``<firstarg>`` is the first parameter of the method (normally ``self``
+for instance methods, and ``cls`` for class methods). For functions
+defined outside a class body, ``__class__`` is not defined, and will
+result in runtime ``SystemError``.
 
-Open Issues
------------
+While ``super`` is not a reserved word, the parser recognizes the use
+of ``super`` in a method definition and only passes in the
+``__class__`` cell when this is found.  Thus, calling a global alias
+of ``super`` without arguments will not necessarily work.
 
+Closed Issues
+=============
 
 Determining the class object to use
-'''''''''''''''''''''''''''''''''''
+-----------------------------------
 
-The exact mechanism for associating the method with the defining class is not
-specified in this PEP, and should be chosen for maximum performance. For
-CPython, it is suggested that the class instance be held in a C-level variable
-on the function object which is bound to one of ``NULL`` (not part of a class),
-``Py_None`` (static method) or a class object (instance or class method).
+The class object is taken from a cell named ``__class__``.
 
 
 Should ``super`` actually become a keyword?
-'''''''''''''''''''''''''''''''''''''''''''
-
-With this proposal, ``super`` would become a keyword to the same extent that
-``None`` is a keyword. It is possible that further restricting the ``super``
-name may simplify implementation, however some are against the actual keyword-
-ization 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 other open issues.
+-------------------------------------------
 
-
-Closed Issues
--------------
+No. It is not necessary for super to become a keyword.
 
 super used with __call__ attributes
-'''''''''''''''''''''''''''''''''''
+-----------------------------------
 
 It was considered that it might be a problem that instantiating super instances
 the classic way, because calling it would lookup the __call__ attribute and
@@ -155,315 +115,8 @@
     assert a() == '__call__'
     assert a.__call__() == '__getattribute__'
 
-In any case, with the renaming of ``__builtin__.super`` to
-``__builtin__.__super__`` this issue goes away entirely.
-
-
-Reference Implementation
-========================
-
-It is impossible to implement the above specification entirely in Python. This
-reference implementation has the following differences to the specification:
-
-1. New ``super`` semantics are implemented using bytecode hacking.
-
-2. Assignment to ``super`` is not a ``SyntaxError``. Also see point #4.
-
-3. Classes must either use the metaclass ``autosuper_meta`` or inherit from
-   the base class ``autosuper`` to acquire the new ``super`` semantics.
-
-4. ``super`` is not an implicit local variable. In particular, for inner
-   functions to be able to use the super instance, there must be an assignment
-   of the form ``super = super`` in the method.
-
-The reference implementation assumes that it is being run on Python 2.5+.
-
-::
-
-    #!/usr/bin/env python
-    #
-    # autosuper.py
-
-    from array import array
-    import dis
-    import new
-    import types
-    import __builtin__
-    __builtin__.__super__ = __builtin__.super
-    del __builtin__.super
-
-    # We need these for modifying bytecode
-    from opcode import opmap, HAVE_ARGUMENT, EXTENDED_ARG
-
-    LOAD_GLOBAL = opmap['LOAD_GLOBAL']
-    LOAD_NAME = opmap['LOAD_NAME']
-    LOAD_CONST = opmap['LOAD_CONST']
-    LOAD_FAST = opmap['LOAD_FAST']
-    LOAD_ATTR = opmap['LOAD_ATTR']
-    STORE_FAST = opmap['STORE_FAST']
-    LOAD_DEREF = opmap['LOAD_DEREF']
-    STORE_DEREF = opmap['STORE_DEREF']
-    CALL_FUNCTION = opmap['CALL_FUNCTION']
-    STORE_GLOBAL = opmap['STORE_GLOBAL']
-    DUP_TOP = opmap['DUP_TOP']
-    POP_TOP = opmap['POP_TOP']
-    NOP = opmap['NOP']
-    JUMP_FORWARD = opmap['JUMP_FORWARD']
-    ABSOLUTE_TARGET = dis.hasjabs
-
-    def _oparg(code, opcode_pos):
-        return code[opcode_pos+1] + (code[opcode_pos+2] << 8)
-
-    def _bind_autosuper(func, cls):
-        co = func.func_code
-        name = func.func_name
-        newcode = array('B', co.co_code)
-        codelen = len(newcode)
-        newconsts = list(co.co_consts)
-        newvarnames = list(co.co_varnames)
-
-        # Check if the global 'super' keyword is already present
-        try:
-            sn_pos = list(co.co_names).index('super')
-        except ValueError:
-            sn_pos = None
-
-        # Check if the varname 'super' keyword is already present
-        try:
-            sv_pos = newvarnames.index('super')
-        except ValueError:
-            sv_pos = None
-
-        # Check if the callvar 'super' keyword is already present
-        try:
-            sc_pos = list(co.co_cellvars).index('super')
-        except ValueError:
-            sc_pos = None
-
-        # If 'super' isn't used anywhere in the function, we don't have anything to do
-        if sn_pos is None and sv_pos is None and sc_pos is None:
-            return func
-
-        c_pos = None
-        s_pos = None
-        n_pos = None
-
-        # Check if the 'cls_name' and 'super' objects are already in the constants
-        for pos, o in enumerate(newconsts):
-            if o is cls:
-                c_pos = pos
-
-            if o is __super__:
-                s_pos = pos
-
-            if o == name:
-                n_pos = pos
-
-        # Add in any missing objects to constants and varnames
-        if c_pos is None:
-            c_pos = len(newconsts)
-            newconsts.append(cls)
-
-        if n_pos is None:
-            n_pos = len(newconsts)
-            newconsts.append(name)
-
-        if s_pos is None:
-            s_pos = len(newconsts)
-            newconsts.append(__super__)
-
-        if sv_pos is None:
-            sv_pos = len(newvarnames)
-            newvarnames.append('super')
-
-        # This goes at the start of the function. It is:
-        #
-        #   super = __super__(cls, self)
-        #
-        # If 'super' is a cell variable, we store to both the
-        # local and cell variables (i.e. STORE_FAST and STORE_DEREF).
-        #
-        preamble = [
-            LOAD_CONST, s_pos & 0xFF, s_pos >> 8,
-            LOAD_CONST, c_pos & 0xFF, c_pos >> 8,
-            LOAD_FAST, 0, 0,
-            CALL_FUNCTION, 2, 0,
-        ]
-
-        if sc_pos is None:
-            # 'super' is not a cell variable - we can just use the local variable
-            preamble += [
-                STORE_FAST, sv_pos & 0xFF, sv_pos >> 8,
-            ]
-        else:
-            # If 'super' is a cell variable, we need to handle LOAD_DEREF.
-            preamble += [
-                DUP_TOP,
-                STORE_FAST, sv_pos & 0xFF, sv_pos >> 8,
-                STORE_DEREF, sc_pos & 0xFF, sc_pos >> 8,
-            ]
-
-        preamble = array('B', preamble)
-
-        # Bytecode for loading the local 'super' variable.
-        load_super = array('B', [
-            LOAD_FAST, sv_pos & 0xFF, sv_pos >> 8,
-        ])
-
-        preamble_len = len(preamble)
-        need_preamble = False
-        i = 0
-
-        while i < codelen:
-            opcode = newcode[i]
-            need_load = False
-            remove_store = False
-
-            if opcode == EXTENDED_ARG:
-                raise TypeError("Cannot use 'super' in function with EXTENDED_ARG opcode")
-
-            # If the opcode is an absolute target it needs to be adjusted
-            # to take into account the preamble.
-            elif opcode in ABSOLUTE_TARGET:
-                oparg = _oparg(newcode, i) + preamble_len
-                newcode[i+1] = oparg & 0xFF
-                newcode[i+2] = oparg >> 8
-
-            # If LOAD_GLOBAL(super) or LOAD_NAME(super) then we want to change it into
-            # LOAD_FAST(super)
-            elif (opcode == LOAD_GLOBAL or opcode == LOAD_NAME) and _oparg(newcode, i) == sn_pos:
-                need_preamble = need_load = True
-
-            # If LOAD_FAST(super) then we just need to add the preamble
-            elif opcode == LOAD_FAST and _oparg(newcode, i) == sv_pos:
-                need_preamble = need_load = True
-
-            # If LOAD_DEREF(super) then we change it into LOAD_FAST(super) because
-            # it's slightly faster.
-            elif opcode == LOAD_DEREF and _oparg(newcode, i) == sc_pos:
-                need_preamble = need_load = True
-
-            if need_load:
-                newcode[i:i+3] = load_super
-
-            i += 1
-
-            if opcode >= HAVE_ARGUMENT:
-                i += 2
-
-        # No changes needed - get out.
-        if not need_preamble:
-            return func
-
-        # Our preamble will have 3 things on the stack
-        co_stacksize = max(3, co.co_stacksize)
-
-        # Conceptually, our preamble is on the `def` line.
-        co_lnotab = array('B', co.co_lnotab)
-
-        if co_lnotab:
-            co_lnotab[0] += preamble_len
-        
-        co_lnotab = co_lnotab.tostring()
-
-        # Our code consists of the preamble and the modified code.
-        codestr = (preamble + newcode).tostring()
-
-        codeobj = new.code(co.co_argcount, len(newvarnames), co_stacksize,
-                           co.co_flags, codestr, tuple(newconsts), co.co_names,
-                           tuple(newvarnames), co.co_filename, co.co_name,
-                           co.co_firstlineno, co_lnotab, co.co_freevars,
-                           co.co_cellvars)
-
-        func.func_code = codeobj
-        func.func_class = cls
-        return func
-
-    class autosuper_meta(type):
-        def __init__(cls, name, bases, clsdict):
-            UnboundMethodType = types.UnboundMethodType
-
-            for v in vars(cls):
-                o = getattr(cls, v)
-                if isinstance(o, UnboundMethodType):
-                    _bind_autosuper(o.im_func, cls)
-
-    class autosuper(object):
-        __metaclass__ = autosuper_meta
-
-    if __name__ == '__main__':
-        class A(autosuper):
-            def f(self):
-                return 'A'
-
-        class B(A):
-            def f(self):
-                return 'B' + super.f()
-
-        class C(A):
-            def f(self):
-                def inner():
-                    return 'C' + super.f()
-
-                # Needed to put 'super' into a cell
-                super = super
-                return inner()
-
-        class D(B, C):
-            def f(self, arg=None):
-                var = None
-                return 'D' + super.f()
-
-        assert D().f() == 'DBCA'
-
-Disassembly of B.f and C.f reveals the different preambles used when ``super``
-is simply a local variable compared to when it is used by an inner function.
-
-::
-
-    >>> dis.dis(B.f)
-
-    214           0 LOAD_CONST               4 (<type 'super'>)
-                  3 LOAD_CONST               2 (<class '__main__.B'>)
-                  6 LOAD_FAST                0 (self)
-                  9 CALL_FUNCTION            2
-                 12 STORE_FAST               1 (super)
-
-    215          15 LOAD_CONST               1 ('B')
-                 18 LOAD_FAST                1 (super)
-                 21 LOAD_ATTR                1 (f)
-                 24 CALL_FUNCTION            0
-                 27 BINARY_ADD          
-                 28 RETURN_VALUE        
-
-::
-
-    >>> dis.dis(C.f)
-
-    218           0 LOAD_CONST               4 (<type 'super'>)
-                  3 LOAD_CONST               2 (<class '__main__.C'>)
-                  6 LOAD_FAST                0 (self)
-                  9 CALL_FUNCTION            2
-                 12 DUP_TOP             
-                 13 STORE_FAST               1 (super)
-                 16 STORE_DEREF              0 (super)
-
-    219          19 LOAD_CLOSURE             0 (super)
-                 22 LOAD_CONST               1 (<code object inner at 00C160A0, file "autosuper.py", line 219>)
-                 25 MAKE_CLOSURE             0
-                 28 STORE_FAST               2 (inner)
-
-    223          31 LOAD_FAST                1 (super)
-                 34 STORE_DEREF              0 (super)
-
-    224          37 LOAD_FAST                2 (inner)
-                 40 CALL_FUNCTION            0
-                 43 RETURN_VALUE        
-
-Note that in the final implementation, the preamble would not be part of the
-bytecode of the method, but would occur immediately following unpacking of
-parameters.
-
+In any case, this issue goes away entirely because classic calls to
+``super(<class>, <instance>)`` are still supported with the same meaning.
 
 Alternative Proposals
 =====================
@@ -491,16 +144,6 @@
 This proposal relies on sys._getframe(), which is not appropriate for anything
 except a prototype implementation.
 
-
-super(__this_class__, self)
----------------------------
-
-This is nearly an anti-proposal, as it basically relies on the acceptance of
-the __this_class__ PEP, 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 [2]_.
-
 self.__super__.foo(\*args)
 --------------------------
 
@@ -527,14 +170,6 @@
 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.
-
 super(\*p, \*\*kw)
 ------------------
 
@@ -561,6 +196,8 @@
 
 History
 =======
+12-Mar-2009 - Updated to reflect the current state of implementation.
+
 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.


More information about the Python-checkins mailing list