2013-05-04 90 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

相關問題