[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