【问题标题】:How to call an overriden superclass method from within a decorated subclass method如何从装饰的子类方法中调用重写的超类方法
【发布时间】:2020-11-24 17:13:46
【问题描述】:

我正在尝试创建一个装饰器,该装饰器在方法被子类覆盖时扩展类方法的功能。这是一个最小的例子:

class Parent(object):
    def __init__(self):
        return

    def say_hello(self):
        print('Parent says hello')

class Child1(Parent):
    def __init__(self):
        super().__init__()

    def say_hello(self):
        print('Child says hello')

child = Child1()
child.say_hello() # this will print 'Child says hello'

我想创建一个装饰器,除了子方法之外,它还将执行父方法。

def extendedmethod(method):
    def wrapper(obj):
        # call the method as defined by the superclass
        method(obj) # call the method defined by the subclass
    return wrapper

class Child2(Parent):
    def __init__(self):
        super().__init__()

    @extendedmethod
    def say_hello(self):
        print('Child says hello')

child = Child2()
child.say_hello() # I want this to print 'Parent says hello' then 'Child says hello'

我想我真正要问的是如何访问从装饰器内部派生子类的超类?

【问题讨论】:

  • 你能修复你的代码吗?第一个不会打印您认为的内容,第二个甚至不会编译为字节码。另外,你知道super吗?
  • 问题在于super需要知道涉及到哪个类和对象,而编译器在extendedmethod内部无权访问这些信息。
  • 我特别要求排除这是否可能是通过在方法中使用super()在方法内解决的XY问题,而不是添加装饰器。
  • 我修复了代码,对此感到抱歉。我只使用过 super 来创建对父类的引用,以便我可以调用它的构造函数。我从来没有在这个上下文之外使用过它,而且我不太了解它。如何在子类的重写方法中使用它?
  • 如果您在print('Child says hello') 之前添加super().say_hello(),您将在没有装饰器的情况下获得所需的行为。这不是你想要的吗?

标签: python inheritance decorator


【解决方案1】:

在显式代码中,我们将编写以下代码:

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)——描述符可以定义一个method __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

【讨论】:

  • 谢谢。我肯定是想多了。
猜你喜欢
  • 2017-07-07
  • 2012-06-15
  • 1970-01-01
  • 2019-11-27
  • 2019-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-18
相关资源
最近更新 更多