装饰器设计:仅在方法被覆盖时执行

Posted

技术标签:

【中文标题】装饰器设计:仅在方法被覆盖时执行【英文标题】:Decorator design: executing a method only if it has been overridden 【发布时间】:2011-09-07 21:08:37 【问题描述】:

我偶然发现了一个棘手的 python 问题。给定(更新):

class A(object):
    def run(self):
        # This makes possible to determine if 'run' was overridden
        if self.run.im_func != A.run.im_func:
            print('Running in 0'.format(self.__class__.__name__))

class B(A):
    def run(self):
        super(B, self).run()

class C(A):
    pass

b = B()
c = C() 
b.run()
>>> Running in B

c.run()
>>> # nothing :)

你将如何设计@runoverriden 装饰器,它可以在 A.run() 中完成条件语句的工作?

更新: 这段代码的目的是 A.run() 应该记录 run() 调用,只有当它被覆盖时。

谢谢!

【问题讨论】:

我给出了一个我认为是您要求的答案,但是您能否更新问题以解释您为什么想要它?可能有一个更好、更简单的解决方案可以为您提供您想要的,而不是您要求的。 看起来你可以简单地改变你的类层次结构,这样“非覆盖”意味着“超类”意味着“超类方法的主体只是pass”。当您可以简单地重新排序超类的子类关系时,为什么所有这些都可以工作? @Duncan,我已经更新了问题的详细信息。如果你还有兴趣,可以看看吗? 虽然您有一个有效的答案,但我认为您应该认真考虑@S.Lott 所说的内容。换句话说,您似乎在倒退。 【参考方案1】:

如果我明白你想要什么:

import functools
def runoverridden(f):
    @functools.wraps(f)
    def wrapper(self, *args, **kw):
        if getattr(self, f.__name__).im_func != wrapper:
            return f(self, *args, **kw)
    return wrapper

class A(object):
    @runoverridden
    def run(self):
        print('Running in A')

class B(A):
    def run(self):
        super(B, self).run()
        print('Running in B')

class C(A):
    pass

b = B()
c = C() 
b.run()
c.run()

【讨论】:

哇!非常感谢你,邓肯!像魅力一样工作。【参考方案2】:

听起来你想要ABCs。具体来说,abstractmethod 装饰器。

【讨论】:

谢谢,关闭,但不完全是我要找的。 ABCMeta 将在代码“构建”阶段抛出异常。虽然@runoverriden 应该停止基类方法调用,但如果它发现该方法没有在子类中被覆盖。底线 - 一个虚假的方法调用。

以上是关于装饰器设计:仅在方法被覆盖时执行的主要内容,如果未能解决你的问题,请参考以下文章

用于覆盖 post 方法的 Django REST 框架自定义装饰器

有没有办法在继承期间持久化装饰器?

烧瓶测试 - 为啥覆盖不包括导入语句和装饰器?

设计模式----装饰器模式

叠加装饰器迭代器

TypeScript 装饰器的执行原理