Correct an oopsie -- Calvin's New Super PEP was already checked in as 367.
This commit is contained in:
parent
868901e5d8
commit
cb39215dad
|
@ -128,7 +128,6 @@ Index by Category
|
||||||
S 3130 Access to Current Module/Class/Function Jewett
|
S 3130 Access to Current Module/Class/Function Jewett
|
||||||
S 3131 Supporting Non-ASCII Identifiers von Löwis
|
S 3131 Supporting Non-ASCII Identifiers von Löwis
|
||||||
S 3132 Extended Iterable Unpacking Brandl
|
S 3132 Extended Iterable Unpacking Brandl
|
||||||
S 3133 New Super Spealman
|
|
||||||
S 3141 A Type Hierarchy for Numbers Yasskin
|
S 3141 A Type Hierarchy for Numbers Yasskin
|
||||||
|
|
||||||
Finished PEPs (done, implemented in Subversion)
|
Finished PEPs (done, implemented in Subversion)
|
||||||
|
@ -503,7 +502,6 @@ Numerical Index
|
||||||
S 3130 Access to Current Module/Class/Function Jewett
|
S 3130 Access to Current Module/Class/Function Jewett
|
||||||
S 3131 Supporting Non-ASCII Identifiers von Löwis
|
S 3131 Supporting Non-ASCII Identifiers von Löwis
|
||||||
S 3132 Extended Iterable Unpacking Brandl
|
S 3132 Extended Iterable Unpacking Brandl
|
||||||
S 3133 New Super Spealman
|
|
||||||
S 3141 A Type Hierarchy for Numbers Yasskin
|
S 3141 A Type Hierarchy for Numbers Yasskin
|
||||||
|
|
||||||
|
|
||||||
|
|
254
pep-3133.txt
254
pep-3133.txt
|
@ -1,254 +0,0 @@
|
||||||
PEP: 3133
|
|
||||||
Title: New Super
|
|
||||||
Version: $Revision$
|
|
||||||
Last-Modified: $Date$
|
|
||||||
Author: Calvin Spealman <ironfroggy@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
|
|
||||||
|
|
||||||
|
|
||||||
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 out "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
|
|
||||||
invokation 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 compatability of the super type API raises some issues. Names, the
|
|
||||||
lookup of the __call__ 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 instansiating 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 seperately may be the best option. A future import or even a simple import
|
|
||||||
in 2.x of the new super type from some builtin module may offer a way to choose
|
|
||||||
which each module uses, even mixing uses by binding to different names. Such a
|
|
||||||
builtin 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 builtin 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.
|
|
||||||
|
|
||||||
|
|
||||||
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'
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
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:
|
|
Loading…
Reference in New Issue