В принципе, вы можете увидеть проблему после ввода кода в интерактивном окне Python:
>>> SubClassAgain
<class '__main__._DecoratedClass'>
Т.е. имя SubClassAgain
теперь привязано (в данном случае в глобальной области видимости) к классу, который на самом деле не"реальный" SubClassAgain
, а является его подклассом. Таким образом, любая ссылка с поздним связыванием на это имя, например та, что у вас есть в вызове super(SubClassAgain,
, конечно же, получит подкласс, который маскируется под этим именем - суперкласс этого подкласса, конечно же, "настоящий SubClassAgain
", откуда бесконечная рекурсия.
Вы можете воспроизвести ту же самую проблему очень просто, без декорирования, просто заставив любой подкласс узурпировать имя своего базового класса:
>>> 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
...
сейчас, Sub().pcl()
вызовет бесконечную рекурсию из-за "узурпации имени". Декорирование класса, если только вы не используете его для декорирования и возврата того же класса, который вы получаете в качестве аргумента, является систематической «узурпацией имени» и, следовательно, несовместимо с использованием имени класса, которое обязательно должно возвращать «истинный» класс этого имени, и не узурпатор (будь то в self
или иначе).
Обходные пути - если вам абсолютно необходимо использовать оба оформления класса в качестве узурпации (а не просто оформление класса изменениями в полученном аргументе класса), и super
- в основном нужны протоколы для взаимодействия между узурпатором и возможный узурпатор, например, следующие небольшие изменения в вашем примере кода:
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()