假设我有一个班级
class Foo:
def __init__(self):
pass
def meth1(self, *args):
do_stuff()
def meth2(self, **kwargs):
more_stuff()
def callme(self):
print "I'm called!"
我希望巧妙地修改它,以便在执行callme()
,meth1
和其他所有内容后调用meth2
。我怎么做?我从来没有使用类装饰器或元类,所以我不确定要走哪条路,以及如何正确使用它们。
对于元类,我把以下内容放在一起:
from types import FunctionType
def addcall_meta(name, bases, dct):
newdct = dict(dct)
for k, v in dct.items():
if isinstance(v, FunctionType) and k not in ('__metaclass__', 'callme'):
# the above can include more filters such as not startswith, etc.
print k, 'is a function!'
def newmethod(self, *args, **kwargs):
v(self, *args, **kwargs)
dct['callme'](self)
newdct[k] = newmethod
return type(name, bases, newdct)
使用类装饰器:
def addcall_deco(cls):
dct = {}
for k, v in cls.__dict__.items():
if isinstance(v, FunctionType) and k != 'callme':
def newv(self, *args, **kwargs):
v(self, *args, **kwargs)
self.callme()
cls.k = newv
return cls
两者似乎都适合我的超简单例子:
In [75]: Foo()
I'm called!
Out[75]: <__main__.Foo instance at 0x21ed680>
是否有明显的理由使用一个而不是另一个(例如特定的角落案例等)?我还想我可以某种方式使用__new__
,但是从共同的角度来看 - 感觉上,我想修改类,而不是它的所有实例。
答案 0 :(得分:2)
旧版本的Python不支持类装饰器,因此如果需要向后兼容性,则需要使用元类或将类装饰器转换为包装类的函数调用。
答案 1 :(得分:1)
实际上你在这里使用你的元类作为类装饰器 - 并不真正需要元类允许的更灵活的东西。
因此,两种实现都是相同的,你应该采用更简单的方法 - 这是类装饰器。至于“向后兼容性” - 在新项目中你不应该担心这个问题。
当然,由于“长期支持”版本,许多服务器都在运行一些非常老的Linux,并且具有2.4或2.5版本的Python版本 - 但是如果 您的项目确实在这样的旧服务器中使用,用户可以随时安装足够的更新以支持作为普通用户的Python - 无需使用系统Python。
在旁注:注意你的包装器,你丢弃包装的方法返回值,如果有的话。做到这一点的方法是:
def newv(self, *args, **kwargs):
result = v(self, *args, **kwargs)
self.callme()
return result
最后,如果您认为在处理任何项目时需要采用这种方法,请查看“Aspect Oriented Programing” - 有几个项目可以实现预期Python中的导向方法,您的工具链或工作方式没有太多变化。