在类中的所有实例方法自动调用之后调用方法:元类还是装饰器?
Posted
技术标签:
【中文标题】在类中的所有实例方法自动调用之后调用方法:元类还是装饰器?【英文标题】:Call a method after all instance method calls in a class automatically: a metaclass or a decorator? 【发布时间】:2012-08-27 02:37:21 【问题描述】:假设我有一堂课
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!"
我想巧妙地修改它,以便在执行meth1
、meth2
和其他所有内容后调用callme()
。我怎么做?我从来没有使用过类装饰器或元类,所以我不确定哪种方法是正确的,也不确定如何正确使用它们。
对于元类,我整理了以下内容:
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__
,但从常识来看看来,我想修改类,而不是它的所有实例。
【问题讨论】:
【参考方案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 中,您的工具链或工作方式没有太多变化。
【讨论】:
非常感谢您的回答!我最终使用了一个装饰器。这实际上并不接近生产代码,我只是想在真正需要处理它们之前熟悉这些问题。丢弃返回值是故意的;这对于特定情况是有意义的。也感谢您的链接!【参考方案2】:旧版本的 Python 不支持类装饰器,因此如果考虑向后兼容性,则需要使用元类或将类装饰器转换为包装类的函数调用。
【讨论】:
其实我对写 Py3 兼容的代码更感兴趣;我有read 我应该在类创建中使用metaclass
关键字;还有什么问题吗?
这是唯一会直接影响这种情况的。
感谢您的回答;也欢迎任何关于代码的 cmets。
考虑使用functools.wraps
。以上是关于在类中的所有实例方法自动调用之后调用方法:元类还是装饰器?的主要内容,如果未能解决你的问题,请参考以下文章