如何将装饰器应用于超类的方法?
Posted
技术标签:
【中文标题】如何将装饰器应用于超类的方法?【英文标题】:How do I apply decorators to a superclass's methods? 【发布时间】:2014-03-20 16:08:23 【问题描述】:假设我有一个 python 类
class Foo(object):
def method1(self, x):
print("method1 called with %s" %(x,))
def method2(self, x):
print("method2 called with %s" %(x,))
和一个装饰器
def myDecorator(func):
newFunc = someModificationOf(func)
return newFunc
我希望能够将Foo
子类化,并且在子类定义中以某种方式表明我希望将myDecorator
应用于从Foo
继承的某些方法子集。例如,我的子类可能喜欢
class Baz(Foo):
methodsToDecorate = ['method2']
这可能吗?
我尝试了什么:
我认为我可以使用元类来做到这一点:
class Baz(Foo):
__metaclass__ = Meta
methodsToDecorate = ['method2']
class Meta(type):
def __new__(cls, name, bases, d):
for m in d['methodsToDecorate']:
d[m] = myDecorator(d[m])
return type(name, bases, d)
但是,它不起作用,因为'method2'
不在传递给元类的__new__
方法的字典中,所以我们得到了KeyError
。我喜欢使用元类的想法,因为它允许装饰器操作函数,而不是方法,这似乎是一个很好的简化。
【问题讨论】:
我不认为你真的想装饰基类方法只是因为创建了一些子类,是吗?这会影响基类的每个实例,以及它的所有独立子类,这会导致代码非常混乱。您是否只想用超类方法的修饰版本覆盖子类中的方法?因为这要简单得多,也更明智。 附带说明,您使用的是 Python 2 风格的元类,但 Python 3 风格的print
方法。您实际使用的是哪种语言?
我使用的是 python 2.7,但习惯于使用 Python 3.x 打印语句,因为它们都适用。
好的,很酷。只是想检查一下。我见过有人问“为什么我的元类不起作用”,因为他们试图在 3.x 中使用 __metaclass__
...
【参考方案1】:
我假设您真正想要做的不是装饰基类的这些方法的副本,而是装饰子类中这些方法的新覆盖副本。
如果是这样,那很容易。这些方法在传递给元类的字典中不存在,因为它们没有在类定义中定义,它们是从基类继承的。元类也可以访问。所以:
def __new__(cls, name, bases, d):
def find_method(m):
for base in bases:
try:
return getattr(base, m)
except AttributeError:
pass
raise AttributeError("No bases have method ''".format(m))
for m in d['methodsToDecorate']:
d[m] = myDecorator(find_method(m))
return type(name, bases, d)
如果你真的想进入基类并替换它的方法,你当然可以使用相同的技巧来做到这一点。只需将 base 与找到的方法一起返回,这样您就可以setattr
它。
您要求重写基类中的方法。但是,如果该方法实际上来自更远的祖先呢?那么这仍然可以工作......但它可能无法按照您想要的方式工作。如果您有多个共享共同祖先的基础,则在每个直接基础上调用 getattr
将在每个基础上搜索该共同祖先一次,但在新类上调用 getattr
只会在最后搜索一次。
正如 user2357112 在评论中指出的那样,如果您想要覆盖的是“如果我没有覆盖任何东西会被调用的任何东西”,您可以通过构造新类 first 来做到这一点,然后通过在结果类本身上调用getattr
来修改它。在 2.x 中,这确实意味着您必须处理未绑定的方法而不是函数(在 3.x 中,这不是问题,因为未绑定的方法只是是函数),但好处可能超过了成本。
【讨论】:
创建类可能会更好,然后装饰方法以便正确遵循 MRO。 @user2357112:同意,但这似乎是对他正在尝试做的事情的单独改进,而不是他所要求的,所以……也许最好离开它在评论中而不是在答案中?你怎么看? @abarnert:完美。另外,感谢您理解我的意思而不是我所说的:) @user2357112:再想一想,我认为它可能确实属于答案。不管怎样,谢谢你提出来。 @abarnert:我认为对于 python 2.x,您应该将 myDecorator(find_method(m)) 更改为 myDecorator(find_method(m).im_func)。那有意义吗?我认为这解决了未绑定方法与函数的问题。以上是关于如何将装饰器应用于超类的方法?的主要内容,如果未能解决你的问题,请参考以下文章