2016-10-07 8 views
6

Próbuję napisać dekorator, który zachowuje argumenty funkcji, które dekoruje. Motywacją do tego jest napisanie dekoratora, który ładnie współdziała z pytest.fixtures.Python tworzenie argumentów funkcji zachowania dekoratora

Załóżmy, że mamy funkcję foo. Wystarczy jeden argument: a.

def foo(a): 
    pass 

Jeśli mamy spec argument foo

>>> inspect.getargspec(foo) 
ArgSpec(args=['a'], varargs=None, keywords=None, defaults=None) 

Często chcemy utworzyć dekorator gdzie funkcja wrapper przechodzi wszystkie jej argumenty verbatim do funkcji wrapped. Najbardziej oczywistym sposobem jest użycie *args i **kwargs.

def identity_decorator(wrapped): 
    def wrapper(*args, **kwargs): 
     return wrapped(*args, **kwargs) 
    return wrapper 

    def identity_decorator(wrapped): 
    def wrapper(*args, **kwargs): 
     return wrapped(*args, **kwargs) 
    return wrapper 

@identity_decorator 
def foo(a): 
    pass 

ten nie jest zaskoczeniem, wywołuje funkcję ze specyfikacją argumentu odzwierciedlającej *args i **kwargs.

>>> inspect.getargspec(foo) 
ArgSpec(args=[], varargs='args', keywords='kwargs', defaults=None) 

Czy istnieje sposób na zmianę argumentu spec, aby dopasować funkcję zawiniętą lub na początku utworzyć funkcję z właściwym argumentem spec?

+2

myślę, że w ostatnich wersjach Pythona, 'functools.wraps' robi coś zrobić' 'inspect.getargspec' i inspekcji .signature' zgłasza podpis zawiniętej funkcji, ale nie zmienia prawdziwego podpisu opakowania. Biblioteka ['decorator'] (https://pypi.python.org/pypi/decorator) udostępnia funkcjonalność dekoratora z zachowaniem sygnatury za pomocą środków, do których nigdy nie zaglądałem, co prawdopodobnie wiąże się z pisaniem kodu bajtowego. Nigdy tego nie używałem. – user2357112

+0

Może sprawdź coś takiego jak http://stackoverflow.com/questions/3729378/how-can-i-programmatically-change-the-argspec-a-function-in-a-python-decorato – Nf4r

Odpowiedz

2

Jak sugerowano w komentarzach, można użyć modułu decorator lub użyć eval złe moce, aby utworzyć funkcję lambda z prawidłowym podpisem:

import inspect 

def identity_decorator(wrapped): 
    argspec = inspect.getargspec(wrapped) 
    args = inspect.formatargspec(*argspec) 

    def wrapper(*args, **kwargs): 

     return wrapped(*args, **kwargs) 

    func = eval('lambda %s: wrapper%s' % (args.strip('()'), args), locals()) 

    return func 

@identity_decorator 
def foo(a): 
    pass 

To trochę hackish, ale zachowuje argumentów funkcji:

>>> inspect.getargspec(foo) 
ArgSpec(args=['a'], varargs=None, keywords=None, defaults=None) 
1

AFAIK, jest możliwe tylko od Pythona 3.3 z obiektem Signature:

def identity_decorator(wrapped): 
    def wrapper(*args, **kwargs): 
     return wrapped(*args, **kwargs) 
    wrapper.__signature__ = inspect.signature(wrapped) # the magic is here! 
    return wrapper 

Następnie można zrobić:

@identity_decorator 
def foo(a): 
    pass 

i wreszcie:

>>> inspect.getargspec(foo) 
ArgSpec(args=['a'], varargs=None, keywords=None, defaults=None) 
Powiązane problemy