2013-05-18 43 views
2

看起來,mymethod尚未調用裝飾器時的方法。如何知道我是否正在裝飾方法

import inspect 

class decorator(object): 
    def __call__(self, call): 
     if inspect.ismethod(call): #Not working yet 
      obj = "method" 
      args = inspect.getargspec(call)[0][1:] 
     elif inspect.isfunction(call): 
      obj = "function" 
      args = inspect.getargspec(call)[0] 
     elif inspect.isclass(call): 
      obj = "class" 
      args = inspect.getargspec(call.__init__)[0][1:] 

     args="(%s)" % repr(args)[1:-1].replace("'","") 
     print "Decorate %s %s%s" % (obj, call.__name__, args) 
     return call 


@decorator() 
def myfunction (a,b): pass 

@decorator() 
class myclass(): 
    def __init__(self, a, b): pass 

    @decorator() 
    def mymethod(self, a, b): pass 

if inspect.isfunction(myclass.mymethod): 
    print "mymethod is a function" 
if inspect.ismethod(myclass.mymethod): 
    print "mymethod is a method" 

輸出:

Decorate function myfunction(a, b) 
Decorate function mymethod(self, a, b) 
Decorate class myclass(a, b) 
mymethod is a method 

我想知道,如果第一個參數是「自我」,但會有一個不太髒的解決方案嗎?

編輯:爲什麼?

我想填充一個可調用列表及其參數,如果它是一個函數或一個類,我可以傳遞期望的參數,然後我調用它,但如果它是一種方法,我沒有「自我「的論點通過。類似於:

import inspect 

class ToDo(object): 
    calls=[] 

    def do(self, **kwargs): 
     for call in self.calls: 
      if 'self' in call.args: 
       print "This will fail." 
      args = {} 
      for arg in call.args: 
       args[arg]=kwargs.get(arg, None) 
      call.call(**args) 

TODO = ToDo() 

class decorator(object): 
    def __call__(self, call): 
     if inspect.isfunction(call): 
      args = inspect.getargspec(call)[0] 
     elif inspect.isclass(call): 
      args = inspect.getargspec(call.__init__)[0][1:] 

     self.call = call 
     self.args = args 
     TODO.calls.append(self) 
     return call 

TODO.do(a=1, b=2) 
+3

爲什麼你需要知道的開始?對於大多數目的來說,一個方法只是另一個可調用的方法,並且可以像非方法函數一樣進行裝飾。 – delnan

+1

同意,不知道你爲什麼需要知道,沒有好的答案。一般來說,如果它真的需要不同,也許你應該爲每種情況使用不同的裝飾器。 –

+0

不要根據是否處理類方法或函數來隱式地更改裝飾器。這與pythonic相反。讓你的裝飾者做同樣的事情(如果你真的**需要這種形式的魔法,請嘗試解釋***你在做什麼!) – Amelia

回答

1

您無法真正地做出區分。這裏有一個例子:

>>> class A(object): 
... pass 
... 
>>> def foo(x): return 3 
... 
>>> A.foo = foo 
>>> type(foo) 
<type 'function'> 
>>> type(A.foo) 
<type 'instancemethod'> 

正如你所看到的,你的裝飾可以適用於foo,因爲它是一個函數。但是你可以簡單地創建一個引用該函數的類屬性來創建一個裝飾方法。

(這個例子是從Python 2.7版;我不知道是否有任何Python 3中已經改變,使上述行爲不同。)

+0

這確實在Python 3中發生了變化。'type(foo)是type(A.foo)',雖然'A()。foo'由於明顯的原因仍然給出了一個綁定方法。 – delnan

+0

瞭解,在該範圍內仍然是一個函數,因爲還沒有被分配爲類方法。 – Txema

0

你不能從功能講的方法,但如果第一個參數看起來你可以檢查喜歡自我:

def __call__(self, func): 
    def new_func(*args, **kw): 
     if len(args) and hasattr(args[0], '__dict__') \ 
       and '__class__' in dir(args[0]) and func.__name__ in dir(args[0])\ 
       and '__func__' in dir(getattr(args[0], func.__name__))\ 
       and getattr(args[0], func.__name__).__func__ == self.func: 
      return my_func(*args[1:], **kw) 
     else: 
      return my_func(*args, **kw) 

    self.func = new_func 
    return new_func 

但是,這不會對嵌套的裝飾工作 - 一個裝飾將改變功能,並與self.func比較將無法工作。

另一種方法 - 檢查裝飾功能的第一個參數的名字是自我 - 這是 在Python很強的命名習慣等都可能是不夠好:

def __call__(self, func): 
    def new_func(*args, **kw): 
     if len(inspect.getfullargspec(func).args)\ 
      and inspect.getfullargspec(func).args[0] == 'self': 
      return my_func(*args[1:], **kw) 
     else: 
      return my_func(*args, **kw) 

    self.func = new_func 
    return new_func 
相關問題