매개 변수가있는 데코레이터?

데코레이터가 변수 ‘insurance_mode’를 전송하는 데 문제가 있습니다. 나는 다음 데코레이터 문장으로 할 것입니다 :

 @execute_complete_reservation(True)
 def test_booking_gta_object(self):
     self.test_select_gta_object()

그러나 불행히도이 진술은 효과가 없습니다. 아마도이 문제를 해결하는 더 좋은 방법이있을 것입니다.

def execute_complete_reservation(test_case,insurance_mode):
    def inner_function(self,*args,**kwargs):
        self.test_create_qsf_query()
        test_case(self,*args,**kwargs)
        self.test_select_room_option()
        if insurance_mode:
            self.test_accept_insurance_crosseling()
        else:
            self.test_decline_insurance_crosseling()
        self.test_configure_pax_details()
        self.test_configure_payer_details

    return inner_function



답변

인수가있는 데코레이터 구문은 약간 다릅니다. 인수가있는 데코레이터는 함수를 가져 오고 다른 함수를 반환 하는 함수 를 반환해야합니다. 따라서 실제로 정상적인 데코레이터를 반환해야합니다. 약간 혼란스럽지 않습니까? 내가 말하고 싶은 건:

def decorator_factory(argument):
    def decorator(function):
        def wrapper(*args, **kwargs):
            funny_stuff()
            something_with_argument(argument)
            result = function(*args, **kwargs)
            more_funny_stuff()
            return result
        return wrapper
    return decorator

여기서 당신은 주제에 대해 더 많은 것을 읽을 수 있습니다-호출 가능한 객체를 사용하여 이것을 구현하는 것도 가능합니다.


답변

편집 : 데코레이터의 정신 모델에 대한 심층적 인 이해를 위해이 멋진 Pycon Talk를 살펴보십시오 . 30 분의 가치가 있습니다.

논쟁이있는 데코레이터에 대해 생각하는 한 가지 방법은

@decorator
def foo(*args, **kwargs):
    pass

로 번역

foo = decorator(foo)

데코레이터가 논쟁을한다면

@decorator_with_args(arg)
def foo(*args, **kwargs):
    pass

로 번역

foo = decorator_with_args(arg)(foo)

decorator_with_args 사용자 정의 인수를 허용하고 실제 데코레이터를 리턴하는 함수입니다 (데코 레이팅 된 함수에 적용됨).

데코레이터를 쉽게 만들기 위해 부분적으로 간단한 트릭을 사용합니다.

from functools import partial

def _pseudo_decor(fun, argument):
    def ret_fun(*args, **kwargs):
        #do stuff here, for eg.
        print ("decorator arg is %s" % str(argument))
        return fun(*args, **kwargs)
    return ret_fun

real_decorator = partial(_pseudo_decor, argument=arg)

@real_decorator
def foo(*args, **kwargs):
    pass

최신 정보:

이상이 foo됩니다real_decorator(foo)

함수를 꾸미는 한 가지 효과는 foo데코레이터 선언시 이름 이 무시 된다는 것 입니다. foo에 의해 반환되는 모든 것에 의해 “재정의”됩니다 real_decorator. 이 경우 새로운 함수 객체입니다.

의 모든 foo메타 데이터, 특히 docstring 및 함수 이름이 재정의됩니다.

>>> print(foo)
<function _pseudo_decor.<locals>.ret_fun at 0x10666a2f0>

functools.wraps 는 docstring과 “name”을 반환 된 함수로 “리프트”하는 편리한 방법을 제공합니다.

from functools import partial, wraps

def _pseudo_decor(fun, argument):
    # magic sauce to lift the name and doc of the function
    @wraps(fun)
    def ret_fun(*args, **kwargs):
        #do stuff here, for eg.
        print ("decorator arg is %s" % str(argument))
        return fun(*args, **kwargs)
    return ret_fun

real_decorator = partial(_pseudo_decor, argument=arg)

@real_decorator
def bar(*args, **kwargs):
    pass

>>> print(bar)
<function __main__.bar(*args, **kwargs)>


답변

IMHO가 매우 우아한 아이디어를 보여주고 싶습니다. t.dubrownik이 제안한 솔루션은 항상 동일한 패턴을 보여줍니다. 데코레이터가하는 일에 관계없이 3 층 래퍼가 필요합니다.

그래서 나는 이것이 메타 데코레이터, 즉 데코레이터를위한 데코레이터의 직업이라고 생각했습니다. 데코레이터는 함수이므로 실제로 인수를 사용하여 일반 데코레이터로 작동합니다.

def parametrized(dec):
    def layer(*args, **kwargs):
        def repl(f):
            return dec(f, *args, **kwargs)
        return repl
    return layer

매개 변수를 추가하기 위해 일반 데코레이터에 적용 할 수 있습니다. 예를 들어 함수의 결과를 두 배로 만드는 데코레이터가 있다고 가정 해보십시오.

def double(f):
    def aux(*xs, **kws):
        return 2 * f(*xs, **kws)
    return aux

@double
def function(a):
    return 10 + a

print function(3)    # Prints 26, namely 2 * (10 + 3)

함께 @parametrized우리는 일반적인 구축 할 수 있습니다 @multiply매개 변수를 가진 장식을

@parametrized
def multiply(f, n):
    def aux(*xs, **kws):
        return n * f(*xs, **kws)
    return aux

@multiply(2)
def function(a):
    return 10 + a

print function(3)    # Prints 26

@multiply(3)
def function_again(a):
    return 10 + a

print function(3)          # Keeps printing 26
print function_again(3)    # Prints 39, namely 3 * (10 + 3)

일반적으로 매개 변수화 된 데코레이터 의 첫 번째 매개 변수 는 함수이고 나머지 인수는 매개 변수화 된 데코레이터의 매개 변수에 해당합니다.

흥미로운 사용법 예제는 형식 안전 독단적 데코레이터입니다.

import itertools as it

@parametrized
def types(f, *types):
    def rep(*args):
        for a, t, n in zip(args, types, it.count()):
            if type(a) is not t:
                raise TypeError('Value %d has not type %s. %s instead' %
                    (n, t, type(a))
                )
        return f(*args)
    return rep

@types(str, int)  # arg1 is str, arg2 is int
def string_multiply(text, times):
    return text * times

print(string_multiply('hello', 3))    # Prints hellohellohello
print(string_multiply(3, 3))          # Fails miserably with TypeError

마지막 참고 사항 : functools.wraps래퍼 함수 에는 사용하지 않지만 항상 사용하는 것이 좋습니다.


답변

다음은 t.dubrownik ‘s answer의 약간 수정 된 버전입니다 . 왜?

  1. 일반적인 템플릿으로 원래 함수에서 반환 값을 반환해야합니다.
  2. 이것은 다른 데코레이터 / 코드에 영향을 줄 수있는 함수의 이름을 변경합니다.

그래서 사용하십시오 @functools.wraps():

from functools import wraps

def decorator(argument):
    def real_decorator(function):
        @wraps(function)
        def wrapper(*args, **kwargs):
            funny_stuff()
            something_with_argument(argument)
            retval = function(*args, **kwargs)
            more_funny_stuff()
            return retval
        return wrapper
    return real_decorator


답변

나는 당신의 문제가 데코레이터에게 인수를 전달한다고 가정합니다. 이것은 약간 까다 롭고 간단하지 않습니다.

이를 수행하는 방법의 예는 다음과 같습니다.

class MyDec(object):
    def __init__(self,flag):
        self.flag = flag
    def __call__(self, original_func):
        decorator_self = self
        def wrappee( *args, **kwargs):
            print 'in decorator before wrapee with flag ',decorator_self.flag
            original_func(*args,**kwargs)
            print 'in decorator after wrapee with flag ',decorator_self.flag
        return wrappee

@MyDec('foo de fa fa')
def bar(a,b,c):
    print 'in bar',a,b,c

bar('x','y','z')

인쇄물:

in decorator before wrapee with flag  foo de fa fa
in bar x y z
in decorator after wrapee with flag  foo de fa fa

자세한 내용은 Bruce Eckel의 기사를 참조하십시오.


답변

def decorator(argument):
    def real_decorator(function):
        def wrapper(*args):
            for arg in args:
                assert type(arg)==int,f'{arg} is not an interger'
            result = function(*args)
            result = result*argument
            return result
        return wrapper
    return real_decorator

데코레이터 사용법

@decorator(2)
def adder(*args):
    sum=0
    for i in args:
        sum+=i
    return sum

그런 다음

adder(2,3)

생산

10

그러나

adder('hi',3)

생산

---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-143-242a8feb1cc4> in <module>
----> 1 adder('hi',3)

<ipython-input-140-d3420c248ebd> in wrapper(*args)
      3         def wrapper(*args):
      4             for arg in args:
----> 5                 assert type(arg)==int,f'{arg} is not an interger'
      6             result = function(*args)
      7             result = result*argument

AssertionError: hi is not an interger


답변

이것은 ()매개 변수 를 제공하지 않아도 필요하지 않은 함수 데코레이터 용 템플리트입니다 .

import functools


def decorator(x_or_func=None, *decorator_args, **decorator_kws):
    def _decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kws):
            if 'x_or_func' not in locals() \
                    or callable(x_or_func) \
                    or x_or_func is None:
                x = ...  # <-- default `x` value
            else:
                x = x_or_func
            return func(*args, **kws)

        return wrapper

    return _decorator(x_or_func) if callable(x_or_func) else _decorator

이에 대한 예는 다음과 같습니다.

def multiplying(factor_or_func=None):
    def _decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if 'factor_or_func' not in locals() \
                    or callable(factor_or_func) \
                    or factor_or_func is None:
                factor = 1
            else:
                factor = factor_or_func
            return factor * func(*args, **kwargs)
        return wrapper
    return _decorator(factor_or_func) if callable(factor_or_func) else _decorator


@multiplying
def summing(x): return sum(x)

print(summing(range(10)))
# 45


@multiplying()
def summing(x): return sum(x)

print(summing(range(10)))
# 45


@multiplying(10)
def summing(x): return sum(x)

print(summing(range(10)))
# 450