파이썬 데코레이터에 추가 인수를 어떻게 전달합니까?


99

아래와 같은 데코레이터가 있습니다.

def myDecorator(test_func):
    return callSomeWrapper(test_func)
def callSomeWrapper(test_func):
    return test_func
@myDecorator
def someFunc():
    print 'hello'

이 데코레이터를 향상시켜 아래와 같은 다른 주장을 받아들이고 싶습니다.

def myDecorator(test_func,logIt):
    if logIt:
        print "Calling Function: " + test_func.__name__
    return callSomeWrapper(test_func)
@myDecorator(False)
def someFunc():
    print 'Hello'

하지만이 코드는 오류를 제공합니다.

TypeError : myDecorator ()는 정확히 2 개의 인수를 사용합니다 (1 개 지정됨).

함수가 자동으로 전달되지 않는 이유는 무엇입니까? 데코레이터 함수에 함수를 명시 적으로 전달하는 방법은 무엇입니까?


3
balki : 부울을 인수로 사용하지 마십시오. 이것은 gd 접근 방식이 아니며 코드의 가독성을 줄입니다
Kit Ho

8
@KitHo-부울 플래그이므로 부울 값을 사용하는 것이 올바른 접근 방식입니다.
AKX

2
@KitHo- "gd"는 무엇입니까? "좋은"가요?
Rob Bednark

답변:


172

데코레이터를 함수처럼 호출하기 때문에 실제 데코레이터 인 다른 함수를 반환해야합니다.

def my_decorator(param):
    def actual_decorator(func):
        print("Decorating function {}, with parameter {}".format(func.__name__, param))
        return function_wrapper(func)  # assume we defined a wrapper somewhere
    return actual_decorator

외부 함수에는 명시 적으로 전달한 모든 인수가 주어지며 내부 함수를 반환해야합니다. 내부 함수는 장식 할 함수를 전달하고 수정 된 함수를 반환합니다.

일반적으로 데코레이터가 래퍼 함수로 래핑하여 함수 동작을 변경하기를 원합니다. 다음은 함수가 호출 될 때 선택적으로 로깅을 추가하는 예입니다.

def log_decorator(log_enabled):
    def actual_decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if log_enabled:
                print("Calling Function: " + func.__name__)
            return func(*args, **kwargs)
        return wrapper
    return actual_decorator

functools.wraps호출은 이름 및 독 스트링과 같은 것을 래퍼 함수에 복사하여 원래 함수와 더 유사하게 만듭니다.

사용 예 :

>>> @log_decorator(True)
... def f(x):
...     return x+1
...
>>> f(4)
Calling Function: f
5

11
그리고 사용 functools.wraps하는 것이 좋습니다-래핑 된 함수의 원래 이름, 독 스트링 등을 유지합니다.
AKX

@AKX : 감사합니다. 두 번째 예제에 추가했습니다.
interjay

1
따라서 기본적으로 데코레이터는 항상 함수 인 하나의 인수 만 취합니다. 그러나 데코레이터는 인수를받을 수있는 함수의 반환 값이 될 수 있습니다. 이 올바른지?
balki

2
@balki : 네, 맞습니다. 혼란스러운 점은 많은 사람들이 외부 함수 ( myDecorator여기서는)를 데코레이터 라고 부르기도한다는 것입니다 . 이것은 데코레이터 사용자에게는 편리하지만 작성하려고 할 때 혼란 스러울 수 있습니다.
interjay

1
나를 혼란스럽게하는 작은 세부 사항 : log_decorator기본 인수를 사용하는 경우 사용할 수 없습니다 @log_decorator.@log_decorator()
Stardidi

46

다른 관점을 제공하기 위해 : 구문

@expr
def func(...): #stuff

다음과 같다

def func(...): #stuff
func = expr(func)

특히 expr콜러 블로 평가되는 한 당신이 좋아하는 무엇이든 될 수 있습니다. 에서 특정 특히, expr실내 장식 공장을 할 수 있습니다 : 당신은 몇 가지 매개 변수를주고 당신에게 장식을 제공합니다. 따라서 상황을 이해하는 더 좋은 방법은

dec = decorator_factory(*args)
@dec
def func(...):

다음으로 단축 할 수 있습니다.

@decorator_factory(*args)
def func(...):

물론 데코레이터 처럼 보이기 때문에 decorator_factory사람들은 그것을 반영하기 위해 이름을 짓는 경향이 있습니다. 간접적 인 수준을 따르려고 할 때 혼란 스러울 수 있습니다.


감사합니다. 이것은 제가 무슨 일이 일어나고 있는지에 대한 근거를 이해하는 데 정말 도움이되었습니다.
Aditya Sriram

25

데코레이터 인수를 선택적으로 만들 수있는 유용한 트릭을 추가하고 싶습니다. 또한 데코레이터를 재사용하고 중첩을 줄일 수 있습니다.

import functools

def myDecorator(test_func=None,logIt=None):
    if not test_func:
        return functools.partial(myDecorator, logIt=logIt)
    @functools.wraps(test_func)
    def f(*args, **kwargs):
        if logIt==1:
            print 'Logging level 1 for {}'.format(test_func.__name__)
        if logIt==2:
            print 'Logging level 2 for {}'.format(test_func.__name__)
        return test_func(*args, **kwargs)
    return f

#new decorator 
myDecorator_2 = myDecorator(logIt=2)

@myDecorator(logIt=2)
def pow2(i):
    return i**2

@myDecorator
def pow3(i):
    return i**3

@myDecorator_2
def pow4(i):
    return i**4

print pow2(2)
print pow3(2)
print pow4(2)

16

데코레이터를 수행하는 또 다른 방법입니다. 이 방법이 내 머리를 감싸는 가장 쉬운 방법이라는 것을 알았습니다.

class NiceDecorator:
    def __init__(self, param_foo='a', param_bar='b'):
        self.param_foo = param_foo
        self.param_bar = param_bar

    def __call__(self, func):
        def my_logic(*args, **kwargs):
            # whatever logic your decorator is supposed to implement goes in here
            print('pre action baz')
            print(self.param_bar)
            # including the call to the decorated function (if you want to do that)
            result = func(*args, **kwargs)
            print('post action beep')
            return result

        return my_logic

# usage example from here on
@NiceDecorator(param_bar='baaar')
def example():
    print('example yay')


example()

감사합니다! 약 30 분 동안 마음을 구부리는 "솔루션"을 살펴 봤는데 이것이 실제로 의미가있는 첫 번째 솔루션입니다.
canhazbits

0

이제 function1데코레이터 로 함수를 호출하고 싶다면 decorator_with_arg,이 경우 함수와 데코레이터 모두 인수를받습니다.

def function1(a, b):
    print (a, b)

decorator_with_arg(10)(function1)(1, 2)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.