2013-09-04 11 views
6

매우 가까운 관련이 있습니다 : How can I programmatically change the argspec of a function in a python decorator?어떻게 프로그래밍 방식으로 파이썬 장식 자에서 함수 *의 argspec을 변경할 수 있습니까?

데코레이터 모듈은 데코 레이팅 된 함수의 argspec를 보존하는 데코레이터 함수를 만드는 방법을 제공합니다.

데코레이터로 사용되지 않는 함수를 정의하면 다른 함수의 argspec를 복사 할 수 있습니까?

예 사용 사례 :

class Blah(object): 
    def foo(self, *args, **kwargs): 
     """ a docstr """ 
     result = bar(*args, **kwargs) 
     result = result**2 # just so it's clear we're doing something extra here... 
     return result 

def bar(x, y, z=1, q=2): 
    """ a more useful docstr, saying what x,y,z,q do """ 
    return x+y*z+q 

나는의 foo '의 bar처럼 argspec의'를하고 싶은,하지만, 소스 (즉, inspect.getsource(foo) 여전히 result 쓰레기를 보여줄 것) 변화가있을 수 있습니다. 이것의 주요 목적은 sphinx 문서와 ipython의 대화 형 도움말을 통해 적절한 인수를 표시하는 것입니다.

다른 질문에 대한 답변에서 말한대로 decorator package은이를 수행 할 수있는 방법을 보여 주지만 그 코드의 일부분에서는 길을 잃었습니다. decorator 패키지가 소스 등을 다시 컴파일하는 것 같습니다. 나는 더 간단한 접근을 기대했다. foo.argspec = bar.argspec과 같은 것이 가능할 것입니다.

+0

그래 그 일부 털이 소스입니다 :

Help on method foo in module __main__: foo(self, x, y, z=1, q=2) unbound __main__.Blah method a more useful docstr, saying what x,y,z,q do 

여기 내가 사용하는 장식입니다 그 데코레이터 함수의 코드. 당신 말이 맞아요, 그 기능을 재건하고 있습니다 - 나는 FunctionMaker를 재사용하려고 제안하고 싶습니다. 클래스 메쏘드를 만들고 어떻게 가는지보십시오. – Hamish

+0

원래의 서명을 개선하거나 적절한 docblock을 추가하는 것보다 더 많은 작업이 필요할 것으로 생각됩니다. – Hamish

+0

'foo'는 실제로 메소드입니다. 그래서'foo'가'staticmethod'가되기를 원하지 않는다면, 첫번째 인자는 항상 인스턴스화 된 객체가 될 것입니다. 그렇지 않으면 'Blah.foo'에 대한 도움말을 확인하고 서명에서 첫 번째 인수로'self '를 보지 않는 것이 혼란 스러울 것입니다. 할 수 없다는 말은 아니며'bar' 시그니처에'self' 접두어를 붙이는 것을 기억해야 할 수도 있습니다. –

답변

4

데코레이터는 단순히 다른 기능으로 무언가를하는 기능입니다. 따라서 기술적으로는 foo 메서드 바로 아래에 필요한 코드를 넣을 수 있습니다. 그러면 기술적으로 장식자를 사용하지 않고 foo을 변경하지만 끔찍한 혼란을 겪을 수 있습니다.

원하는 작업을 수행하는 가장 쉬운 방법은 복사 할 서명을 알 수 있도록 두 번째 함수 (이 경우 bar)를 인수로 사용하는 데코레이터를 만드는 것입니다. 당신은 bar 전에 대신 클래스 후에 정의해야 할 것이다

class Blah(object): 
    @copy_argspec(bar) 
    def foo(self, *args, **kwargs): 
     """ a docstr """ 
     result = bar(*args, **kwargs) 
     result = result**2 # just so it's clear we're doing something extra here... 
     return result 

: 클래스 코드는 다음과 같이 보일 것입니다.

.
.
.
. . . 시간이으로 전달됩니다. . . .
.
.

좋아요, 운 좋게도 저는 적응할 수있는 오래된 데코레이터를 발견했습니다.

help(Blah.foo)

장식하기 전에 다음과 같습니다

Help on method foo in module __main__: 

foo(self, *args, **kwargs) unbound __main__.Blah method 
    a docstr 

과 장식 후에는 다음과 같습니다

import inspect 

class copy_argspec(object): 
    """ 
    copy_argspec is a signature modifying decorator. Specifically, it copies 
    the signature from `source_func` to the wrapper, and the wrapper will call 
    the original function (which should be using *args, **kwds). The argspec, 
    docstring, and default values are copied from src_func, and __module__ and 
    __dict__ from tgt_func. 
    """ 
    def __init__(self, src_func): 
     self.argspec = inspect.getargspec(src_func) 
     self.src_doc = src_func.__doc__ 
     self.src_defaults = src_func.func_defaults 

    def __call__(self, tgt_func): 
     tgt_argspec = inspect.getargspec(tgt_func) 
     need_self = False 
     if tgt_argspec[0][0] == 'self': 
      need_self = True 

     name = tgt_func.__name__ 
     argspec = self.argspec 
     if argspec[0][0] == 'self': 
      need_self = False 
     if need_self: 
      newargspec = (['self'] + argspec[0],) + argspec[1:] 
     else: 
      newargspec = argspec 
     signature = inspect.formatargspec(
       formatvalue=lambda val: "", 
       *newargspec 
       )[1:-1] 
     new_func = (
       'def _wrapper_(%(signature)s):\n' 
       ' return %(tgt_func)s(%(signature)s)' % 
       {'signature':signature, 'tgt_func':'tgt_func'} 
        ) 
     evaldict = {'tgt_func' : tgt_func} 
     exec new_func in evaldict 
     wrapped = evaldict['_wrapper_'] 
     wrapped.__name__ = name 
     wrapped.__doc__ = self.src_doc 
     wrapped.func_defaults = self.src_defaults 
     wrapped.__module__ = tgt_func.__module__ 
     wrapped.__dict__ = tgt_func.__dict__ 
     return wrapped 
+0

그래서'copy_argspec'은 데코레이터와 거의 같을 것이라고 제안합니다. ,'bar'를'foo' 대신 argspec의 소스로 사용하겠습니까? 나는 그 아이디어를 좋아하고 시도 할 것이다. – keflavich

+1

@keflavich : 당신이 붙어 있다면, 나는 장식자를 추가했다. :) –

관련 문제