将默认参数传递给python中的装饰器

Mik*_*olk 11 python arguments decorator python-decorators

我试图找到一种方法将我的函数的默认参数传递给装饰器。我不得不说我对装饰器业务相当陌生,所以也许我只是没有正确理解它,但我还没有找到任何答案。

所以这是我从 Pythonfunctools.wraps手册页修改的示例。

from functools import wraps
def my_decorator(f):
    @wraps(f)
    def wrapper(*args, **kwds):
            print('Calling decorated function')
            print('args:', args)
            print('kwargs:', kwds)
            return f(*args, **kwds)
    return wrapper

@my_decorator
def example(i, j=0):
    """Docstring"""
    print('Called example function')

example(i=1)
Run Code Online (Sandbox Code Playgroud)

我也希望j=0通过。所以输出应该是:

Calling decorated function
args: ()
kwargs: {'i': 1, 'j': 0}
Called example function
Run Code Online (Sandbox Code Playgroud)

但相反我得到

Calling decorated function
args: ()
kwargs: {'i': 1}
Called example function
Run Code Online (Sandbox Code Playgroud)

Rec*_*nic 8

获取 args 和 kwargs 的确切列表有点棘手,因为您可以将位置参数作为 kwarg 传递,反之亦然。新版本的 python 还添加了仅位置参数或仅关键字参数。

但是,inspect.signature有一种可以应用默认值的机制:调用.bind(*args, **kwargs)后跟.apply_defaults(). 这可以为您提供一本有效地包含该函数的所有参数的字典。在OP的例子中,这变成:

from functools import wraps
import inspect
def my_decorator(f):
    sig = inspect.signature(f)
    @wraps(f)
    def wrapper(*args, **kwds):
        bound = sig.bind(*args, **kwds)
        bound.apply_defaults()
        print('Calling decorated function')
        print('called with:', bound.arguments)
        return f(*args, **kwds)
    return wrapper

@my_decorator
def example(i, j=0):
    """Docstring"""
    print('Called example function')

example(i=1)
Run Code Online (Sandbox Code Playgroud)

在 Python 3.9 上输出以下内容:

Calling decorated function
called with: OrderedDict([('i', 1), ('j', 0)])
Called example function
Run Code Online (Sandbox Code Playgroud)


小智 6

默认参数是函数签名的一部分。它们不存在于装饰器调用中。

要在包装器中访问它们,您需要将它们从函数中取出,如本问题所示。

import inspect
from functools import wraps

def get_default_args(func):
    signature = inspect.signature(func)
    return {
        k: v.default
        for k, v in signature.parameters.items()
        if v.default is not inspect.Parameter.empty
    }

def my_decorator(f):
    @wraps(f)
    def wrapper(*args, **kwds):
            print('Calling decorated function')
            print('args:', args)
            kwargs = get_default_args(f)
            kwargs.update(kwds)
            print('kwargs:', kwargs)
            return f(*args, **kwds)
    return wrapper

@my_decorator
def example(i, j=0):
    """Docstring"""
    print('Called example function')

example(i=1)
Run Code Online (Sandbox Code Playgroud)

输出:

Calling decorated function
args: ()
kwargs: {'i': 1, 'j': 0}
Called example function
Run Code Online (Sandbox Code Playgroud)

  • 调用“example(1)”会给出“kwargs: {'j': 0}”,但缺少“i”键,这可能是一个问题。 (2认同)

Sha*_*hul 4

您可以使用__defaults__特殊属性获取默认参数值。

def my_decorator(f):
@wraps(f)
def wrapper(*args, **kwds):
    print('def args values', f.__defaults__)
    return f(*args, **kwds)
return wrapper
Run Code Online (Sandbox Code Playgroud)

__defaults__参考:在https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy中查找

包含具有默认值的参数的默认参数值的元组,如果没有参数具有默认值,则包含 None