[Python-ideas] superarg: an alternative to super()

Guido van Rossum guido at python.org
Wed Apr 9 04:00:56 CEST 2008


Sorry, not interested.

On Tue, Apr 8, 2008 at 6:55 PM, Anthony Tolle <artomegus at gmail.com> wrote:
> I was testing the new functionality of super() in Python 3.0a4, and
>  noted the current "limitations":
>
>  ------------------------------------------------------------
>  Test 1 - define method outside class definition
>
>  >>> class A:
>  ...  def f(self):
>  ...   return 'A'
>  ...
>  >>> def B_f(self):
>  ...  return super().f() + 'B'
>  ...
>  >>> class B(A):
>  ...  f = B_f
>  ...
>  >>> B().f()
>  Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "<stdin>", line 2, in B_f
>  SystemError: super(): __class__ cell not found
>
>  ------------------------------------------------------------
>  Test 2 - Call super() from inner function
>
>  >>> class A:
>  ...  def f(self):
>  ...   return 'A'
>  ...
>  >>> class B(A):
>  ...  def f(self):
>  ...   def inner():
>  ...    return super().f() + 'B'
>  ...   return inner()
>  ...
>  >>> B().f()
>  Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "<stdin>", line 5, in f
>   File "<stdin>", line 4, in inner
>  SystemError: super(): no arguments
>
>  ------------------------------------------------------------
>
>  Not satisfied, I started work on another way to simplify using
>  super().  I call it superarg, and it doesn't have any of the above
>  limitations.  It only takes twenty-six lines to implement in Python,
>  and doesn't rely on bytecode hacks or stack frame inspection.
>
>  What superarg does do is provide a decorator that inserts the super
>  object into the method's argument list, before the self argument.  In
>  the following Python-only implementation, correct behavior does depend
>  on using a special metaclass.
>
>  Here it is, including a test suite (tested in Python 3.0a4):
>
>  ------------------------------------------------------------
>  # superarg.py
>
>  from types import MethodType
>
>  class superarg_method():
>     def __init__(self, callable, cls=None):
>         self.callable = callable
>         self.cls = cls
>     def __get__(self, obj, objtype=None):
>         return self if obj is None else MethodType(self, obj)
>     def __call__(self, obj, *args, **kwargs):
>         return self.callable(super(self.cls, obj), obj, *args, **kwargs)
>     def withclass(self, cls):
>         return self.__class__(self.callable, cls)
>
>  class superarg_classmethod(superarg_method):
>     def __get__(self, obj, objtype=None):
>         return MethodType(self, type(obj) if objtype is None else objtype)
>
>  class superarg_meta(type):
>     def __init__(cls, name, bases, dict):
>         for name, value in dict.items():
>             if isinstance(value, superarg_method):
>                 type.__setattr__(cls, name, value.withclass(cls))
>     def __setattr__(cls, name, value):
>         if isinstance(value, superarg_method):
>             value = value.withclass(cls)
>         type.__setattr__(cls, name, value)
>
>  # implementation ends here - test suite follows:
>
>  if __name__ == '__main__':
>     class A(metaclass=superarg_meta):
>         def f(self):
>             return 'A(%s)' % (self.name,)
>         @classmethod
>         def cm(cls):
>             return 'A(%s)' % (cls.__name__,)
>
>     # Standard use
>     class B(A):
>         @superarg_method
>         def f(super, self):
>             return 'B' + super.f()
>         @superarg_classmethod
>         def cm(super, cls):
>             return 'B' + super.cm()
>
>     # Reference super in inner function
>     class C(A):
>         @superarg_method
>         def f(super, self):
>             def inner():
>                 return 'C' + super.f()
>             return inner()
>         @superarg_classmethod
>         def cm(super, cls):
>             def inner():
>                 return 'C' + super.cm()
>             return inner()
>
>     # Define functions before class definition
>     @superarg_method
>     def D_f(super, self):
>         return 'D' + super.f()
>
>     @superarg_classmethod
>     def D_cm(super, cls):
>         return 'D' + super.cm()
>
>     class D(B, C):
>         def __init__(self, name):
>             self.name = name
>         f = D_f
>         cm = D_cm
>
>     # Define functions after class definition
>     class E(C, B):
>         def __init__(self, name):
>             self.name = name
>
>     @superarg_method
>     def E_f(super, self):
>         return 'E' + super.f()
>
>     @superarg_classmethod
>     def E_cm(super, cls):
>         return 'E' + super.cm()
>
>     E.f = E_f
>     E.cm = E_cm
>
>     # Test D
>     d = D('d')
>     assert d.f()  == 'DBCA(d)' # Normal method, instance binding
>     assert D.f(d) == 'DBCA(d)' # Normal method, class binding
>     assert d.cm() == 'DBCA(D)' # Class method, instance binding
>     assert D.cm() == 'DBCA(D)' # Class method, class binding
>
>     # Test E
>     e = E('e')
>     assert e.f()  == 'ECBA(e)'
>     assert E.f(e) == 'ECBA(e)'
>     assert e.cm() == 'ECBA(E)'
>     assert E.cm() == 'ECBA(E)'
>
>     # Test using D's methods in E
>     E.cm = D_cm
>     E.f = D_f
>     assert e.f()  == 'DCBA(e)'
>     assert E.f(e) == 'DCBA(e)'
>     assert e.cm() == 'DCBA(E)'
>     assert E.cm() == 'DCBA(E)'
>
>     # Why not use E.cm = D.cm?  Because D.cm returns a bound method
>     # (this would also be the case without using superarg).  Instead,
>     # one would need to use E.cm = D.__dict__['cm']
>
>  ------------------------------------------------------------
>
>  The decorators only need to be applied where necessary.  If a method
>  doesn't need the super object, then it doesn't need the decorator.
>
>  I also tested implementing the decorators in C (as built-in types),
>  which works even better because I was able to put the class fix-ups in
>  type_new and type_setattro.  Thus, the special metaclass is no longer
>  needed.
>
>  The only times it wouldn't work right is if the end-user implemented a
>  metaclass that never calls type's __new__ or __setattr__ methods.
>  Avoiding type.__new__ should be fairly rare, but I'm not sure about
>  __setattr__.  I could probably extend the types so users can do their
>  own fix-ups if need be.  However, if a user is customizing a class
>  that much, then I wonder if they would make use of superarg anyway.
>
>  Whether or not there is any perceived value in adding this to a future
>  version of Python, perhaps someone will find it useful enough to add
>  to one of their own projects (and back-porting it to Python 2.x should
>  be fairly simple).
>
>  --
>  Anthony Tolle
>  _______________________________________________
>  Python-ideas mailing list
>  Python-ideas at python.org
>  http://mail.python.org/mailman/listinfo/python-ideas
>



-- 
--Guido van Rossum (home page: http://www.python.org/~guido/)



More information about the Python-ideas mailing list