В явном коде мы бы написали следующее:
class Child2(Parent):
def say_hello(self):
super().say_hello() # call baseclass method
print('Child says hello')
Форма super()
без аргументов компилируется в super(__class__, self)
здесь, где __class__ = Child2
. Важной частью является то, что super
требует как экземпляр self
, так и , владеющий классом .
Получить экземпляр просто - он передается декорированному методу при вызове. Задача состоит в том, чтобы извлечь класс, в котором находится декорированный метод.
Один из подходов - разработать наш декоратор как дескриптор (аналогично property
) - дескрипторы могут определять метод __set_name__
, чтобы получить свое имя и класс владельца . Кроме того, мы должны определить __get__
, чтобы удовлетворить протоколу дескриптора и получить self
, а также __call__
для фактического вызова методов:
class extendedmethod:
"""Decorator to call the superclass method before a method"""
def __init__(self, method, owner=None):
self.method = method
self.owner = owner
def __set_name__(self, owner, name):
self.owner = owner
def __get__(self, instance, owner=None):
# self.__call__ with `__m_self__` and `__m_cls__` filled in
return partial(self, instance, owner)
def __call__(self, __m_self__, __m_cls__, *args, **kwargs):
# self: the decorator instance
# __m_self__: the `self` seen by a method
# __m_cls__: the `cls` seen by a classmethod
# super(__class__, self).say_hello ------------------------v
# v super(__class__, self) ----v
getattr(super(self.owner, __m_self__), self.method.__name__)(*args, **kwargs)
return self.method.__get__(__m_self__, __m_cls__)(*args, **kwargs)
Этот декоратор может быть непосредственно применен к методу для вызова его метода суперкласса:
class Child2(Parent):
@extendedmethod
def say_hello(self):
print('Child says hello')
Child2().say_hello()
# Parent says hello
# Child says hello