2010-01-05 6 views
0

오브젝트 obj을 사용하는 새로운 함수/메소드를 작성하는 데코레이터를 만들고 싶습니다. 데코 레이팅 된 객체가 함수 인 경우 함수를 만들 때 obj을 인스턴스화해야합니다. 데코 레이팅 된 객체가 메서드 인 경우 새 obj을 인스턴스화하고 메서드가 데코 레이팅 된 클래스의 각 인스턴스에 바인딩해야합니다. 데코레이터가 함수 문서를 수정하기 때문에 장식을 __init__에 넣을 수 없습니다. 지금은 이런 일을 가지고 있지만 그것은 단지 내가 원하는하지 않은, 한 번 time을 인스턴스화인스턴스화 시간 변수가있는 파이썬 데코레이터?

__all__ = ['dec', 'A'] 

from time import time 
import inspect 

def dec(f): 
    obj = time() # want to set on object instantiation 
    def new(*args, **kwargs): 
     f(*args, **kwargs) # Validate against definition so it doesn't go 
          # out of sync 
     print obj 
     # ... 
    try: 
     d = inspect.getsourcelines(f) 
    except IOError: 
     d = "<unable to fetch definition>" 
    else: 
     d = d[0][1].rstrip('\n').rstrip(':').lstrip(' ').lstrip('def') 
    new.__doc__ = d + "\n" + (f.__doc__ or '') 
    return new 

class A(object): 
    @dec 
    def f(self, x): 
     """something""" 
     print '%s.f(%s)' % (self, x) 

if __name__ == '__main__': 
    A().f(123) 
    A().f(123) 
    A().f(123) 
이 문제를 해결하기 위해 내 생각은 장식에 전달 된 객체가 인수 self 걸리는 경우 만약 그렇다면, 확인하는 것입니다

이미 존재하지 않으면 objself에 바인드하고 self.obj을 사용하는 메소드를 리턴하십시오. 그리고 장식 자에게 건네지는 객체에 대해 self 인수가 없으면 장식 자 안에 obj을 인스턴스화하고이를 사용하는 함수를 반환하십시오.

그러나 ... 내 진짜 장식 자 때문에 list에서 파생되고 __call__ 특성을 가진 개체를 반환하기 때문에 내가 말한 것은 실제로 나를 위해 작동하지 않습니다. 또한 실제 장식 자의 경우 self은 인스턴스 변수를 사용하지 않기 때문에 장식 된 객체에 정의되어 있지 않습니다. (내가 무엇인지 실제로 장식은 외부 객체가 구독하는 이벤트이며, 이벤트에는 서명이 문서화되어 있음).

편집 : 사실 list 하위 클래스 인스턴스가 인스턴스에 바인딩되도록하여 __call__ 속성이 클래스 인스턴스를 암시 적으로 (예 : 일반 인스턴스 메소드에서와 같이) 수신하는 경우 완벽한 솔루션이 될 것입니다. 내가 원래하는 방법을 알아 내려고 애썼다. 하지만 self 특성을 사용하여 데코 레이팅 된 메서드를 정의 할 필요가없는 더 나은 솔루션이있을 수 있습니까? 둘 중 하나가 완벽합니다.

답변

1

데코레이터는 객체 생성자에서 그렇게 할 이유

def func(): 
    ... 
func = decorator(func) 

말을 단지 문법 설탕이기 때문에?

class A(object): 
    def __init__(self): 
     # apply decorator at instance creation 
     self.f = dec(self.f) 

    def f(self, x): 
     """something""" 
     print '%s.f(%s)' % (self, x) 
+0

나는 분명히 그것이 내가 원하는 것이 아니라고 말했다. 내 데코레이터는 데코 레이팅 된 함수의 문서를 변경합니다. 데코레이션을'__init__'에 넣으면 발생하지 않습니다. –

+0

@ Longpoke : 왜'__doc__'가 중요한가요? '__doc__'의 일부를 생성하기 위해 메타 클래스를 사용하지 않는 이유 –

+0

데코레이터는이 함수를'list'의 서브 클래스로 변환하고'__call__'을 추가합니다. 그건'__doc__'을 파괴하므로 새로 만든 객체로 복사합니다. 나는 메타 클래스에 대한 경험이 없지만 여기서 도움이 될지 확신하지 못합니다. 이미 abc.ABCMeta를 사용하고 있는데 한 번에 두 개의 메타 클래스를 사용할 수 있습니까? 필자가 정말로 필요로하는 것은'self '를 받기위한'__call__' 메소드 (리스트 서브 클래스에 추가)를위한 것입니다. 그래서 나는 상태를 유지할 수 있습니다. –

0

글쓰기 스타일을 읽기가 정말 어렵습니다. 보통 문장의 길이는 귀하의 절반입니다 : P

원하십니까?

__all__ = ['dec', 'A'] 

from time import time, sleep 
import inspect 

def dec(f): 
    def new(self, *args, **kwargs): 
     print self.initiated # print the time the objecte was initiated ... 
     return f(self, *args, **kwargs) # Validate against definition so it doesn't go 
          # out of sync 
    try: 
     d = inspect.getsourcelines(f) 
    except IOError: 
     d = "<unable to fetch definition>" 
    else: 
     d = d[0][1].rstrip('\n').rstrip(':').lstrip(' ').lstrip('def') 
    new.__doc__ = d + "\n" + (f.__doc__ or '') 
    return new 

class A(object): 
    def __init__(self): 
     self.initiated = time() # save the time the object was initiated 


    @dec 
    def f(self, x): 
     """something""" 
     print '%s.f(%s)' % (self, x) 

if __name__ == '__main__': 
    A().f(123) 
    sleep(1) 
    A().f(123) 
    sleep(1) 
    A().f(123) 
+0

데코 레이팅 된 함수를 후속 호출 할 때 'obj'인스턴스가 다르기 때문에이 방법은 작동하지 않습니다. 필자는 예제에서 동일한 객체가 참조되고 있음을 보여주기 위해'time()'만을 사용했습니다. 나는'id()'를 사용 했어야했다. –

1

정확하게 무엇을했는지 알아내는 것은 약간 어렵습니다. 목록 및 __call__ 물건은 내가 주로 첫 번째 단락을 고수하고있어 나를 혼란 :

__all__ = ['dec', 'A'] 

from types import InstanceType 
from functools import wraps 
import inspect 

def dec(func): 

    #get the sig of the function 
    sig = [] 
    @wraps(func) 
    def wrapper(*args, **kwargs): 
     ret = None 
     #if this is a method belonging to an object... 
     if args and getattr(args[0], func.__name__, None): 
     instance, args = args[0], args[1:] 
     #if sig of object is not already set 
     if not hasattr(instance, "sig"): 
      instance.sig = [] 
     ret = func(instance, *args, **kwargs) 
     print "Sig of %s is %s" % (func.__name__, id(instance.sig)) 
     #else this is a function 
     else: 
     ret = func(*args, **kwargs) 
     print "Sig of %s is %s" % (func.__name__, id(sig)) 
     return ret 

    #modify the doc string 
    try: 
     docs = inspect.getsourcelines(func) 
    except: 
     docs = "<unable to fetch defintion>" 
    else: 
     docs = docs[0][1].rstrip('\n').rstrip(':').lstrip(' ').lstrip('def') 
    wrapper.__doc__ = docs + "\n" + (func.__doc__ or '') 
    return wrapper 

class A(object): 
    def __init__(self): 
     super(A, self).__init__() 

    @dec 
    def f(self, x): 
     """something""" 
     print '%s.f(%s)' % (self, x) 


@dec 
def myfunc(): 
    print "myfunc" 

@dec 
def myfunc2(): 
    print "myfunc2" 

@dec 
def myfunc3(): 
    print "myfunc3" 

if __name__ == "__main__": 
    list = [] 
    for x in xrange(3): 
     list.append(A()) 

    [a.f(123) for a in list] 
    myfunc() 
    myfunc() 
    myfunc2() 
    myfunc2() 
    myfunc3() 
    myfunc3() 

는 출력 :

<__main__.A object at 0x00B9F2D0>.f(123) 
Sig of f is 11932616 
<__main__.A object at 0x00B9F430>.f(123) 
Sig of f is 11925464 
<__main__.A object at 0x00B9F450>.f(123) 
Sig of f is 11918112 
myfunc 
Sig of myfunc is 11925624 
myfunc 
Sig of myfunc is 11925624 
myfunc2 
Sig of myfunc2 is 11794592 
myfunc2 
Sig of myfunc2 is 11794592 
myfunc3 
Sig of myfunc3 is 11925144 
myfunc3 
Sig of myfunc3 is 11925144 
+0

시간을 시그마로 사용할 때 코드가 너무 빨리 실행되어 항상 같았습니다 ... 그래서 ID를 사용하도록 전환했지만 그 다음에는 func의 ID/instance는이 코드가 가정하고있는 것을 수행하지 않더라도 동일합니다. 빈리스트를 만들고 "sig"의 각 인스턴스의 id를 출력하는 것으로 정했습니다. – manifest

+0

ㅎ, 거의 작동합니다. 첫 번째 인수의 비교는 오히려 위험합니다. isinstance (x, InstanceType)는 구식 클래스의 인스턴스에 대해서만 True를 반환하기 때문에 코드가 손상됩니다. 그 비교를 제거하면 그것을 수정하므로 A의 각 인스턴스는 다른 "sig"id를 출력합니다. 중첩 된 데코레이터로이 작업을 시도하고 있는데 데코 레이팅 된 메서드 내부의 데코 레이팅 된 메서드 클래스에 대한 참조를 닫아 비교할 수 있습니다. 또한 'wraps'을 사용한 이후로 문서를 더 이상 복사 할 필요가 없습니다. 그 중 하나를 주셔서 감사합니다, 저에게 몇 가지 코드를 저장합니다 :) –

+0

args가 [] 인 경우 args가 if 문을 단락하기 때문에 첫 번째 인수의 비교가 위험하지 않습니다. 공지 사항 myfuncs에는 args가 없지만 코드는 중단되지 않습니다. 새로운 스타일을 깨뜨리는 것이 옳습니다. 잘 알고 있습니다. 나는 당신이 그걸로 펑키 한 것을하고 있었기 때문에 나는 단지 문서를 복사했다. 위의 코드를 작업 데모로 업데이트했습니다. – manifest

0

좋아 매니페스트의 코드를 기반으로, 나는 해결책을 가지고있다.당신이 볼 수 있듯이

, pydoc이 여전히 장식 된 일에 대해 괜찮은 문서를해야합니다 :

class A(__builtin__.object) 
| Methods defined here: 
| 
| f(*args, **kwargs) 
|  f(self, x) 
|  something 

뿐만 아니라, 장식 방법으로 클래스의 각 인스턴스가 다른 obj이있을 것이다. 또한 각각의 기능에는 각각 obj이 있습니다. 여기
<__main__.A object at 0x7c209bee4a50>.f(123) 
obj of <__main__.A object at 0x7c209bee4a50>.f is 136479497243752 
<__main__.A object at 0x7c209bee4a10>.f(123) 
obj of <__main__.A object at 0x7c209bee4a10>.f is 136479497250720 
<__main__.A object at 0x7c209bee4a90>.f(123) 
obj of <__main__.A object at 0x7c209bee4a90>.f is 136479497446824 
myfunc 
obj of myfunc is 136479497243392 
myfunc 
obj of myfunc is 136479497243392 
myfunc2 
obj of myfunc2 is 136479497245688 
myfunc2 
obj of myfunc2 is 136479497245688 
myfunc3 
obj of myfunc3 is 136479497246408 
myfunc3 
obj of myfunc3 is 136479497246408 

코드입니다 :

추가 된 외부 장식, cls는 클래스의 ID를 가져 오는 데 사용됩니다, 그래서 장식 기능은 확실히 날씨 말할 수
__all__ = ['dec', 'A'] 

from functools import wraps 
import inspect 

def dec(cls=None): 
    # cls will be closed in subdec 
    def subdec(func): 
     # closed in wrapper, guaranteed to be unique per decorator evaluation 
     obj = [] 

     @wraps(func) 
     def wrapper(*args, **kwargs): 
      if (args and type(args[0]) == cls): 
       instance = args[0] 
       # We will attach to instance a dict _objs of 
       # function_name:obj. This way we don't pollute the namespace 
       # when decorating many functions. 

       # Alternatively, you could make a dict external to instance 
       # of instance:{function_name:obj}, but that takes more work 
       # because you need to make it not prevent garbage collection 
       # of instance. 
       if not hasattr(instance, "_objs"): 
        instance._objs = {} 
       if func.__name__ not in instance._objs: 
        instance._objs[func.__name__] = [] 
       func(*args, **kwargs) # This is only used to check the arity. 
             # My real code is all to do with 
             # manipulating obj. 
       print "obj of %s.%s is %s" % (
        instance, 
        func.__name__, 
        id(instance._objs[func.__name__]) 
       ) 
      else: 
       # Functions are identified by the closed obj 
       func(*args, **kwargs) 
       print "obj of %s is %s" % (func.__name__, id(obj)) 

     # Find function/method signature and prepend it to the new object's doc 
     try: 
      doc = inspect.getsourcelines(func) 
     except IOError: 
      line = "<unable to fetch definition>" 
     else: 
      line = '@' 
      i = 0 
      while line.lstrip(' ').startswith("@"): 
       try: 
        line = doc[0][i] 
       except IndexError: 
        line = "<unable to fetch definition>" 
       i += 1 
     line = line.rstrip('\n').rstrip(':').lstrip(' ').lstrip('def') 
     wrapper.__doc__ = line + "\n" + (func.__doc__ or '') 

     return wrapper 
    return subdec 

class A(object): 
    def f(self, x): 
     """something""" 
     print '%s.f(%s)' % (self, x) 

A.f = dec(A)(A.f) 

@dec() 
def myfunc(): 
    print "myfunc" 

@dec() 
def myfunc2(): 
    print "myfunc2" 

@dec() 
def myfunc3(): 
    print "myfunc3" 

if __name__ == "__main__": 
    a, b, c = A(), A(), A() 
    # a, b, and c each have their own instance of obj: 
    a.f(123) 
    b.f(123) 
    c.f(123) 
    myfunc() 
    myfunc() 
    myfunc2() 
    myfunc2() 
    myfunc3() 
    myfunc3() 

그 함수 또는 메소드입니다. 나는 이것이 얼마나 잘 상속받을 지 확신하지 못한다. 아마도 그 부분을 확인하기위한 매니페스트의 생각이 더 나을 것이다.

관련 문제