I'm trying to figure out how to use decorators on subclasses that use super()
. Since my class decorator creates another subclass a decorated class seems to prevent the use of super()
when it changes the className
passed to super(className, self)
. Below is an example:
def class_decorator(cls):
class _DecoratedClass(cls):
def __init__(self):
return super(_DecoratedClass, self).__init__()
return _DecoratedClass
class BaseClass(object):
def __init__(self):
print "class: %s" % self.__class__.__name__
def print_class(self):
print "class: %s" % self.__class__.__name__
bc = BaseClass().print_class()
class SubClass(BaseClass):
def print_class(self):
super(SubClass, self).print_class()
sc = SubClass().print_class()
@class_decorator
class SubClassAgain(BaseClass):
def print_class(self):
super(SubClassAgain, self).print_class()
sca = SubClassAgain()
# sca.print_class() # Uncomment for maximum recursion
The output should be:
class: BaseClass
class: BaseClass
class: SubClass
class: SubClass
class: _DecoratedClass
Traceback (most recent call last):
File "class_decorator_super.py", line 34, in <module>
sca.print_class()
File "class_decorator_super.py", line 31, in print_class
super(SubClassAgain, self).print_class()
...
...
RuntimeError: maximum recursion depth exceeded while calling a Python object
Does anyone know of a way to not break a subclass that uses super()
when using a decorator? Ideally I'd like to reuse a class from time to time and simply decorate it w/out breaking it.
Basically, you can see the problem after entering your code sample at the interactive Python prompt:
>>> SubClassAgain
<class '__main__._DecoratedClass'>
i.e., the name SubClassAgain
is now bound (in global scope, in this case) to a class that in fact isn't the "real" SubClassAgain
, but a subclass thereof. So, any late-bound reference to that name, like the one you have in its super(SubClassAgain,
call, will of course get the subclass that's masquerading by that name -- that subclass's superclass is of course "the real SubClassAgain
", whence the infinite recursion.
You can reproduce the same problem very simply without decoration, just by having any subclass usurp its base-class's name:
>>> class Base(object):
... def pcl(self): print 'cl: %s' % self.__class__.__name__
...
>>> class Sub(Base):
... def pcl(self): super(Sub, self).pcl()
...
>>> Sub().pcl()
cl: Sub
>>> class Sub(Sub): pass
...
now, Sub().pcl()
will cause infinite recursion, due to the "name usurpation". Class decoration, unless you use it to decorate and return the same class you get as an argument, is systematic "name usurpation", and thus incompatible with uses of the class name which absolutely must return the "true" class of that name, and not the usurper (be that in self
or otherwise).
Workarounds -- if you absolutely must have both class decoration as usurpation (not just class decoration by changes in the received class argument), and super
-- basically need protocols for cooperation between the usurper and the possible-usurpee, such as the following small changes to your example code:
def class_decorator(cls):
class _DecoratedClass(cls):
_thesuper = cls
def __init__(self):
return super(_DecoratedClass, self).__init__()
return _DecoratedClass
...
@class_decorator
class SubClassAgain(BaseClass):
def print_class(self):
cls = SubClassAgain
if '_thesuper' in cls.__dict__:
cls = cls._thesuper
super(cls, self).print_class()
The decorator creates a kind-of diamond inheritance situation. You can avoid these problems by not using super()
. Changing SubClassAgain
to the following will prevent infinite recursion:
@class_decorator
class SubClassAgain(BaseClass):
def print_class(self):
BaseClass.print_class(self)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With