假设我有一个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
。我喜欢使用元类的想法,因为它允许装饰器操作函数而不是方法,这似乎是一个很好的简化。
答案 0 :(得分:2)
我假设你真正想做的事情并不是装饰基类的那些方法的副本,而是装饰子类中这些方法的新重写副本。
如果是这样,那很容易。传递给元类的字典中不存在这些方法,因为它们没有在类定义中定义,它们是从基类继承的。哪个元类也可以访问。所以:
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)
如果你确实想要进入基类并替换它的方法,你当然可以使用相同的技巧。只需返回基础以及找到的方法,您就可以setattr
。
您要求覆盖基类中的方法。但是,如果该方法实际上来自更远的祖先呢?然后这仍然有效...但它可能无法按您想要的方式工作。如果您有多个共享共同祖先的基础,则在每个直接基础上调用getattr
将在每个基础上搜索该共同祖先一次,但在新类上调用getattr
将仅在结束时搜索一次。
正如user2357112在评论中指出的那样,如果您要覆盖的是“如果我没有覆盖任何东西就会被调用的任何内容”,您可以通过首先构建新类来实现这一点 ,然后通过在结果类本身上调用getattr
来修改它。在2.x中,这意味着你必须处理未绑定的方法而不是函数(在3.x中,这不是问题,因为未绑定的方法只是是函数),但是好处可能超过了成本。