不,没有钩子类可以做到这一点。方法也是的属性,虽然有些特殊,因为它们是在访问实例上的函数对象时产生的;函数是descriptors。
对方法对象的调用是一个独立于生成方法对象的步骤:
>>> class Foo(object):
... def bar(self):
... return 'bar method on Foo'
...
>>> f = Foo()
>>> f.bar
<bound method Foo.bar of <__main__.Foo object at 0x100777bd0>>
>>> f.bar is f.bar
False
>>> stored = f.bar
>>> stored()
'bar method on Foo'
调用描述符协议是object.__getattribute__() method 的任务,因此您可以挂钩以查看何时生成方法,但您仍然需要包装该生成方法检测调用的对象。例如,您可以返回一个带有 __call__ method 的对象,该对象代表实际方法。
但是,使用每次调用时递增计数器的装饰器来装饰每个方法会更容易。考虑到装饰器应用于函数在绑定之前,所以你必须传递self:
from functools import wraps
def method_counter(func):
@wraps(func)
def wrapper(self, *args, **kwargs):
self.methodCalls += 1
return func(self, *args, **kwargs)
return wrapper
您仍然需要将此应用于您班级中的所有函数。您可以手动将其应用于您要计算的所有方法:
class MyClass(object):
def __init__(self):
self.foo = "foo"
self.bar = "bar"
self.methodCalls = 0 #tracks number of times any function method is run
@method_counter
def get_foo(self):
return self.foo
@method_counter
def get_bar(self):
return self.bar
或者你可以使用元类:
import types
class MethodCounterMeta(type):
def __new__(mcls, name, bases, body):
# create new class object
for name, obj in body.items():
if name[:2] == name[-2:] == '__':
# skip special method names like __init__
continue
if isinstance(obj, types.FunctionType):
# decorate all functions
body[name] = method_counter(obj)
return super(MethodCounterMeta, mcls).__new__(mcls, name, bases, body)
def __call__(cls, *args, **kwargs):
# create a new instance for this class
# add in `methodCalls` attribute
instance = super(MethodCounterMeta, cls).__call__(*args, **kwargs)
instance.methodCalls = 0
return instance
这会处理装饰器所需的一切,为您设置 methodCalls 属性,因此您的类不必:
class MyClass(object):
__metaclass__ = MethodCounterMeta
def __init__(self):
self.foo = "foo"
self.bar = "bar"
def get_foo(self):
return self.foo
def get_bar(self):
return self.bar
后一种方法的演示:
>>> class MyClass(object):
... __metaclass__ = MethodCounterMeta
... def __init__(self):
... self.foo = "foo"
... self.bar = "bar"
... def get_foo(self):
... return self.foo
... def get_bar(self):
... return self.bar
...
>>> instance = MyClass()
>>> instance.get_foo()
'foo'
>>> instance.get_bar()
'bar'
>>> instance.methodCalls
2
上面的元类只考虑类主体的函数对象(所以def语句和lambda表达式的结果)部分进行装饰。它忽略任何其他可调用对象(还有更多类型具有__call__ 方法,例如functools.partial 对象),以及稍后添加到类的函数。