2013-05-04 85 views
14

我正在寻找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

那些不委托给dunder方法的函数呢? – delnan 2013-05-04 08:28:02

+0

你可能想看看类装饰器和元类。 – 2013-05-04 08:37:09

+0

@delnan我想,这些也是可以的,因为在我的情况下,这些函数正在寻找别的东西,一个值或一个方法来调用。 – 2013-05-04 08:37:49

回答

6

元类在这里没有帮助;尽管在当前对象的类型上查找了特殊的方法(所以类为实例),但这样做时可能不会查询__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__明确,我们还是迷上了进去。

+0

由于我是'@ decorators'的新手,我不得不阅读这篇文章(http://www.artima.com/weblogs/viewpost.jsp?thread=240808 ),这非常简单,然后我就明白了,你做了什么......现在我必须承认,我知道发生了什么 - 这不再是魔术了:)我重新实现了你的解决方案在装饰类中 - 我想,更容易跟随我的代码中发生的事情。 – 2013-05-04 11:30:30

+0

@PeterVaro:那很好。 :-)我的答案的重点是如何生成dunder-method名称列表。 :-P – 2013-05-04 12:28:16

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' 

现在所有test1test2呼叫将在callslist寄存器。

decorate_methods施加装饰器类的每个方法。 register_calls将调用注册到calls中的方法,并使用函数的名称和参数。

+0

但是,这仍然需要你首先在课堂上创建所有特殊的方法。 – 2013-05-04 09:08:23

+0

@morphyn是的,Martijn Pieters是对的,我刚刚测试过这个 - 也许我没有正确使用它 - 但我无法做到我想要的... – 2013-05-04 09:11:32

+0

是的,你仍然需要创建方法。我不明白你想要什么。你正在寻找ruby的'method_missing'然后:)然后你将不得不使用'__getattr__'。 – 2013-05-04 09:25:18

相关问题