[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