2013-05-18 16 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 

내가 첫 번째 인수가 '자기'가 있는지 알고,하지만 덜 더러운 해결책이 될 것인가?

편집 : 왜?

함수 또는 클래스 인 경우 callable 및 해당 인수 목록을 채우고 예상되는 인수를 전달할 수 있습니다. 그런 다음이를 호출하지만 메서드 인 경우에는 " 자기 "인수를 전달합니다. 다음과 같이 입력하십시오.

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

정말 구별 할 수 없습니다. 다음은 그 예이다 : 당신이 볼 수 있듯이, 당신의 장식이 foo에 적용 할 수

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

,이 기능 때문이다. 그런 다음 함수를 참조하는 데코 레이팅 된 메서드를 만드는 클래스 특성을 간단하게 만들 수 있습니다.

(이 예는 파이썬 2.7에서이다, 나는 확실하지 않다 무엇이든 다르게 위없이 행동을하는 파이썬 3에서 변경된 경우.) 당신은 함수에서 방법을 말할 수 있지만, 당신은 첫 번째 인수의 외모 경우 확인할 수 있습니다

+0

이것은 실제로 파이썬 3에서 변경되었습니다.'A(). foo'는 분명한 이유로 묶인 메소드를 제공하지만'type (foo)는 type (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와 기능과 비교가 작동하지 않습니다 변경됩니다.

또 다른 방법 - 장식 된 함수의 첫 번째 인수의 이름이 자기인지 확인합니다 - 이것은 파이썬에서 매우 강력한 규칙이 너무 충분있을 수 있습니다 :

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 
관련 문제