我正在寻找一个关于如何存储在对象内部的对象上调用的方法的pythonic解决方案。
因为在python中,如果我想捕获例如abs()
方法,我将重载此运算符,如:
Catcher(object):
def __abs__(self):
self.function = abs
c = Catcher()
abs(c) # Now c.function stores 'abs' as it was called on c
如果我想要捕获一个具有其他属性的函数,例如pow()
,我将使用它:
Catcher(object):
def __pow__(self, value):
self.function = pow
self.value = value
c = Catcher()
c ** 2 # Now c.function stores 'pow', and c.value stores '2'
现在,我正在寻找的是一个通用的解决方案,用于捕获和存储在Catcher
上调用的任何类型的函数,而不实现所有重载和其他情况。正如您所看到的,我还想存储值(可能在列表中,如果有多个?),这些是方法的属性。
提前致谢!
答案 0 :(得分:7)
元类在这里没有帮助;虽然查找了当前对象类型的特殊方法(因此实例的类),但在执行此操作时不会查询__getattribute__
或__getattr__
(可能因为它们本身就是特殊方法)。因此,要捕获所有 dunder方法,您必须全部创建它们。
您可以通过枚举operator
module来获得所有运算符特殊方法(__pow__
,__gt__
等)的相当不错的列表:
import operator
operator_hooks = [name for name in dir(operator) if name.startswith('__') and name.endswith('__')]
使用该列表,类装饰器可以是:
def instrument_operator_hooks(cls):
def add_hook(name):
operator_func = getattr(operator, name.strip('_'), None)
existing = getattr(cls, name, None)
def op_hook(self, *args, **kw):
print "Hooking into {}".format(name)
self._function = operator_func
self._params = (args, kw)
if existing is not None:
return existing(self, *args, **kw)
raise AttributeError(name)
try:
setattr(cls, name, op_hook)
except (AttributeError, TypeError):
pass # skip __name__ and __doc__ and the like
for hook_name in operator_hooks:
add_hook(hook_name)
return cls
然后将其应用于您的班级:
@instrument_operator_hooks
class CatchAll(object):
pass
演示:
>>> c = CatchAll()
>>> c ** 2
Hooking into __pow__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in op_hook
AttributeError: __pow__
>>> c._function
<built-in function pow>
>>> c._params
((2,), {})
因此,即使我们的课程没有明确定义__pow__
,我们仍然会加入其中。
答案 1 :(得分:2)
这是一种方法。
import inspect
from functools import wraps
from collections import namedtuple
call = namedtuple('Call', ['fname', 'args', 'kwargs'])
calls = []
def register_calls(f):
@wraps(f)
def f_call(*args, **kw):
calls.append(call(f.__name__, args, kw))
print calls
return f(*args, **kw)
return f_call
def decorate_methods(decorator):
def class_decorator(cls):
for name, m in inspect.getmembers(cls, inspect.ismethod):
setattr(cls, name, decorator(m))
return cls
return class_decorator
@decorate_methods(register_calls)
class Test(object):
def test1(self):
print 'test1'
def test2(self):
print 'test2'
现在,test1
和test2
的所有来电都将成为calls
list
中的注册。
decorate_methods
将装饰器应用于类的每个方法。 register_calls
使用函数名和参数注册calls
中方法的调用。