[Python-Dev] New Super PEP

Calvin Spealman ironfroggy at gmail.com
Sun Apr 29 19:30:25 CEST 2007


On 4/29/07, Calvin Spealman <ironfroggy at gmail.com> wrote:
> Draft Attempt Number Duo:
>
> PEP: XXX
> 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
>
>
> 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:
>

I just realized I could change a small part of this. If we do the two
super types thing, where we keep the original unchanged and make a
completely different, new super type, and you can pick which you use,
that simplifies tons of issues with the new super. The builtin name
'super' can refer to an instance of some NewSuper type, making its
__getattr__ way simpler, etc.

I still wasn't really aware of any alternative suggestions that need
to be included in this.

-- 
Read my blog! I depend on your acceptance of my opinion! I am interesting!
http://ironfroggy-code.blogspot.com/


More information about the Python-Dev mailing list