Movatterモバイル変換


[0]ホーム

URL:


Following system colour schemeSelected dark colour schemeSelected light colour scheme

Python Enhancement Proposals

PEP 367 – New Super

Author:
Calvin Spealman <ironfroggy at gmail.com>,Tim Delaney <timothy.c.delaney at gmail.com>
Status:
Superseded
Type:
Standards Track
Created:
28-Apr-2007
Python-Version:
2.6
Post-History:
28-Apr-2007,29-Apr-2007,29-Apr-2007,14-May-2007

Table of Contents

Numbering Note

This PEP has been renumbered toPEP 3135. The text below is the lastversion submitted under the old number.

Abstract

This PEP proposes syntactic sugar for use of thesuper type to automaticallyconstruct instances of the super type binding to the class that a method wasdefined in, and the instance (or class object for classmethods) that the methodis currently acting upon.

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)

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 tosuper become aSyntaxError,similar to the behaviour ofNone.

Rationale

The current usage of super requires an explicit passing of both the class andinstance it must operate from, requiring a breaking of the DRY (Don’t RepeatYourself) rule. This hinders any change in class name, and is often considereda wart by many.

Specification

Within the specification section, some special terminology will be used todistinguish similar and closely related concepts. “super type” will refer tothe actual builtin type named “super”. A “super instance” is simply an instanceof the super type, which is associated with a class and possibly with aninstance of that class.

Because the newsuper semantics are not backwards compatible with Python2.5, the new semantics will require a__future__ import:

from__future__importnew_super

The current__builtin__.super will be aliased to__builtin__.__super__.This will occur regardless of whether the newsuper semantics are active.It is not possible to simply rename__builtin__.super, as that would affectmodules that do not use the newsuper semantics. In Python 3.0 it isproposed that the name__builtin__.super will be removed.

Replacing the old usage of super, calls to the next class in the MRO (methodresolution order) can be made without explicitly creating asuperinstance (although doing so will still be supported via__super__). Everyfunction will have an implicit local namedsuper. This name behavesidentically to a normal local, including use by inner functions via a cell,with the following exceptions:

  1. Assigning to the namesuper will raise aSyntaxError at compile time;
  2. Calling a static method or normal function that accesses the namesuperwill raise aTypeError at runtime.

Every function that uses the namesuper, or has an inner function thatuses the namesuper, will include a preamble that performs the equivalentof:

super=__builtin__.__super__(<class>,<instance>)

where<class> is the class that the method was defined in, and<instance> is the first parameter of the method (normallyself forinstance methods, andcls for class methods). For static methods and normalfunctions,<class> will beNone, resulting in aTypeError beingraised during the preamble.

Note: The relationship betweensuper and__super__ is similar to thatbetweenimport and__import__.

Much of this was discussed in the thread of the python-dev list, “Fixing superanyone?”[1].

Open Issues

Determining the class object to use

The exact mechanism for associating the method with the defining class is notspecified in this PEP, and should be chosen for maximum performance. ForCPython, it is suggested that the class instance be held in a C-level variableon the function object which is bound to one ofNULL (not part of a class),Py_None (static method) or a class object (instance or class method).

Shouldsuper actually become a keyword?

With this proposal,super would become a keyword to the same extent thatNone is a keyword. It is possible that further restricting thesupername may simplify implementation, however some are against the actualkeyword-ization of super. The simplest solution is often the correct solutionand the simplest solution may well not be adding additional keywords to thelanguage when they are not needed. Still, it may solve other open issues.

Closed Issues

super used with __call__ attributes

It was considered that it might be a problem that instantiating super instancesthe classic way, because calling it would lookup the __call__ attribute andthus try to perform an automatic super lookup to the next class in the MRO.However, this was found to be false, because calling an object only looks upthe __call__ method directly on the object’s type. The following example showsthis in action.

classA(object):def__call__(self):return'__call__'def__getattribute__(self,attr):ifattr=='__call__':returnlambda:'__getattribute__'a=A()asserta()=='__call__'asserta.__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. Thisreference implementation has the following differences to the specification:

  1. Newsuper semantics are implemented using bytecode hacking.
  2. Assignment tosuper is not aSyntaxError. Also see point #4.
  3. Classes must either use the metaclassautosuper_meta or inherit fromthe base classautosuper to acquire the newsuper semantics.
  4. super is not an implicit local variable. In particular, for innerfunctions to be able to use the super instance, there must be an assignmentof the formsuper=super in the method.

The reference implementation assumes that it is being run on Python 2.5+.

#!/usr/bin/env python## autosuper.pyfromarrayimportarrayimportdisimportnewimporttypesimport__builtin____builtin__.__super__=__builtin__.superdel__builtin__.super# We need these for modifying bytecodefromopcodeimportopmap,HAVE_ARGUMENT,EXTENDED_ARGLOAD_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.hasjabsdef_oparg(code,opcode_pos):returncode[opcode_pos+1]+(code[opcode_pos+2]<<8)def_bind_autosuper(func,cls):co=func.func_codename=func.func_namenewcode=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 presenttry:sn_pos=list(co.co_names).index('super')exceptValueError:sn_pos=None# Check if the varname 'super' keyword is already presenttry:sv_pos=newvarnames.index('super')exceptValueError:sv_pos=None# Check if the cellvar 'super' keyword is already presenttry:sc_pos=list(co.co_cellvars).index('super')exceptValueError:sc_pos=None# If 'super' isn't used anywhere in the function, we don't have anything to doifsn_posisNoneandsv_posisNoneandsc_posisNone:returnfuncc_pos=Nones_pos=Nonen_pos=None# Check if the 'cls_name' and 'super' objects are already in the constantsforpos,oinenumerate(newconsts):ifoiscls:c_pos=posifois__super__:s_pos=posifo==name:n_pos=pos# Add in any missing objects to constants and varnamesifc_posisNone:c_pos=len(newconsts)newconsts.append(cls)ifn_posisNone:n_pos=len(newconsts)newconsts.append(name)ifs_posisNone:s_pos=len(newconsts)newconsts.append(__super__)ifsv_posisNone: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,]ifsc_posisNone:# 'super' is not a cell variable - we can just use the local variablepreamble+=[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=Falsei=0whilei<codelen:opcode=newcode[i]need_load=Falseremove_store=Falseifopcode==EXTENDED_ARG:raiseTypeError("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.elifopcodeinABSOLUTE_TARGET:oparg=_oparg(newcode,i)+preamble_lennewcode[i+1]=oparg&0xFFnewcode[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_GLOBALoropcode==LOAD_NAME)and_oparg(newcode,i)==sn_pos:need_preamble=need_load=True# If LOAD_FAST(super) then we just need to add the preambleelifopcode==LOAD_FASTand_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.elifopcode==LOAD_DEREFand_oparg(newcode,i)==sc_pos:need_preamble=need_load=Trueifneed_load:newcode[i:i+3]=load_superi+=1ifopcode>=HAVE_ARGUMENT:i+=2# No changes needed - get out.ifnotneed_preamble:returnfunc# Our preamble will have 3 things on the stackco_stacksize=max(3,co.co_stacksize)# Conceptually, our preamble is on the `def` line.co_lnotab=array('B',co.co_lnotab)ifco_lnotab:co_lnotab[0]+=preamble_lenco_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=codeobjfunc.func_class=clsreturnfuncclassautosuper_meta(type):def__init__(cls,name,bases,clsdict):UnboundMethodType=types.UnboundMethodTypeforvinvars(cls):o=getattr(cls,v)ifisinstance(o,UnboundMethodType):_bind_autosuper(o.im_func,cls)classautosuper(object):__metaclass__=autosuper_metaif__name__=='__main__':classA(autosuper):deff(self):return'A'classB(A):deff(self):return'B'+super.f()classC(A):deff(self):definner():return'C'+super.f()# Needed to put 'super' into a cellsuper=superreturninner()classD(B,C):deff(self,arg=None):var=Nonereturn'D'+super.f()assertD().f()=='DBCA'

Disassembly of B.f and C.f reveals the different preambles used whensuperis 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 thebytecode of the method, but would occur immediately following unpacking ofparameters.

Alternative Proposals

No Changes

Although its always attractive to just keep things how they are, people havesought a change in the usage of super calling for some time, and for goodreason, all mentioned previously.

  • Decoupling from the class name (which might not even be bound to theright class anymore!)
  • Simpler looking, cleaner super calls would be better

Dynamic attribute on super type

The proposal adds a dynamic attribute lookup to the super type, which willautomatically determine the proper class and instance parameters. Each superattribute lookup identifies these parameters and performs the super lookup onthe instance, as the current super implementation does with the explicitinvocation of a super instance upon a class and instance.

This proposal relies on sys._getframe(), which is not appropriate for anythingexcept a prototype implementation.

super(__this_class__, self)

This is nearly an anti-proposal, as it basically relies on the acceptance ofthe __this_class__ PEP, which proposes a special name that would always bebound 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 namebinding issues[2].

self.__super__.foo(*args)

The __super__ attribute is mentioned in this PEP in several places, and couldbe a candidate for the complete solution, actually using it explicitly insteadof any super usage directly. However, double-underscore names are usually aninternal 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 handledifferently named super methods, and is explicit about the name of theinstance. It is less flexible without being able to enacted on other methodnames, in cases where that is needed. One use case this fails is where abase-class has a factory classmethod and a subclass has two factoryclassmethods, both of which needing to properly make super calls to the onein the base-class.

super.foo(self, *args)

This variation actually eliminates the problems with locating the properinstance, and if any of the alternatives were pushed into the spotlight, Iwould want it to be this one.

super or super()

This proposal leaves no room for different names, signatures, or applicationto other classes, or instances. A way to allow some similar use alongside thenormal proposal would be favorable, encouraging good design of multipleinheritance trees and compatible methods.

super(*p, **kw)

There has been the proposal that directly callingsuper(*p,**kw) wouldbe equivalent to calling the method on thesuper object with the same nameas the method currently being executed i.e. the following two methods would beequivalent:

deff(self,*p,**kw):super.f(*p,**kw)
deff(self,*p,**kw):super(*p,**kw)

There is strong sentiment for and against this, but implementation and styleconcerns are obvious. Guido has suggested that this should be excluded fromthis PEP on the principle of KISS (Keep It Simple Stupid).

History

29-Apr-2007 - Changed title from “Super As A Keyword” to “New Super”
  • Updated much of the language and added a terminology sectionfor clarification in confusing places.
  • Added reference implementation and history sections.
06-May-2007 - Updated by Tim Delaney to reflect discussions on the python-3000
and python-dev mailing lists.

References

[1]
Fixing super anyone?(https://mail.python.org/pipermail/python-3000/2007-April/006667.html)
[2]
PEP 3130: Access to Module/Class/Function Currently Being Defined (this)(https://mail.python.org/pipermail/python-ideas/2007-April/000542.html)

Copyright

This document has been placed in the public domain.


Source:https://github.com/python/peps/blob/main/peps/pep-0367.rst

Last modified:2025-02-01 08:59:27 GMT


[8]ページ先頭

©2009-2025 Movatter.jp