1. ホーム
  2. python

[解決済み] Pythonのデコレータに追加の引数を渡すには?

2022-08-30 18:24:21

質問

以下のようなデコレータがあります。

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つは指定されています)

なぜ関数は自動的に渡されないのでしょうか?デコレータ関数に明示的に関数を渡すにはどうしたらよいでしょうか。

どのように解決するのですか?

デコレータを関数のように呼び出しているので、実際のデコレータである別の関数を返す必要があります。

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 の呼び出しは、名前とdocstringのようなものをラッパー関数にコピーし、元の関数とより似たものにします。

使用例です。

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