Sorry, not interested.
On Tue, Apr 8, 2008 at 6:55 PM, Anthony Tolle
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@python.org http://mail.python.org/mailman/listinfo/python-ideas
-- --Guido van Rossum (home page: http://www.python.org/~guido/)