我有一个通用类,用户应该对其进行子类化以实现某些方法。可以有几个深度的子类。类似的东西
class Thing(object):
def fun(self, *args, **kwargs):
raise NotImplementedError()
class Bell(Thing):
def fun(self):
return 1
class Whistle(Bell):
def fun(self):
return super(Whistle, self).fun() + 1
我想计算使用 Thing
的任何子类时调用 fun()
的次数。因为装饰器不是继承的,并且因为我不希望用户必须记住装饰他们的 fun() 方法,所以我的理解是元类是正确的选择。所以我写了
class CountCalls(type):
def __new__(cls, name, bases, attrs):
attrs["_original_fun"] = attrs["fun"]
attrs["fun"] = countcalls(attrs["_original_fun"])
return super(CountCalls, cls).__new__(cls, name, bases, attrs)
其中 countcalls
是用于计算调用次数的经典装饰器:
def countcalls(fn):
def wrapper(*args, **kwargs):
wrapper.ncalls += 1
return fn(*args, **kwargs)
wrapper.ncalls = 0
wrapper.__name__ = fn.__name__
wrapper.__doc__ = fn.__doc__
return wrapper
并将 Thing
的定义更改为
class Thing(object):
__metaclass__ = CountCalls
def fun(self, *args, **kwargs):
raise NotImplementedError()
问题:这可行,但它会产生意想不到的副作用,即在 fun()
时增加所有实例的调用计数器数量> 调用任何实例的方法:
>>> b1 = Bell()
>>> b2 = Bell()
>>> b1.fun.ncalls, b2.fun.ncalls
(0, 0)
>>> b1.fun()
1
>>> b1.fun.ncalls, b2.fun.ncalls
(1, 1)
问题:如何计算每个实例对 fun()
的调用次数?感觉我应该在元类中实现 __init__
而不是 __new__
,但到目前为止我还没有找到正确的语法。例如,使用
def __init__(self, name, bases, attrs):
attrs["_original_fun"] = attrs["fun"]
attrs["fun"] = countcalls(attrs["_original_fun"])
super(CountCalls, self).__init__(name, bases, attrs)
产量
>>> b = Bell()
>>> b.fun.ncalls
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'ncalls'
谢谢!
最佳答案
您可以通过稍微更改继承模式来跳过元类:
class Thing(object):
def __init__(self):
self.fun_calls = 0
def fun(self, *args, **kwargs):
self.fun_calls += 1
self._fun(*args, **kwargs)
def _fun(self, *args, **kwargs):
raise NotImplementedError()
然后只需在子类中覆盖 _fun
即可。这可以让你对每个实例进行自动计数,并且它(我认为)比元类实现更干净、更容易理解。
关于python - 计算对类和任何子类的某些方法的调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36319757/