Python中验证的函数参数

Validated Function Arguments in Python

我有一个功能

1
2
def func(a,b,c,d):
    ...

我正在尝试编写一个能理解这些参数并将其中一些参数记录到另一个系统中的修饰符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def decorator(func):
    def new_func(*args, **kwargs):
        if (func.__name__ == 'func'):
            a = ?
            b = ?
            c = ?
            d = ?
        else:
            a = ?
            b = ?
            c = ?
            d = ?
        log_to_system(a, b, c, d)
        return func(*args, **kwargs)
return new_func

问题是,装饰器没有一种简单的方法从args和kwargs中提取a、b、c、d值,因为用户可以使用位置参数或关键字参数传递这些值。我还希望保持这个通用的,因为这个修饰器可以用于各种不同的函数。

是否有一个库或实用程序可以轻松地从args和kwargs中提取参数值?


一个简单的方法是让你的log_to_system函数除了它实际记录的已知参数外,还接受变量参数和变量关键字参数,这样你就可以简单地把变量参数和变量关键字参数从修饰函数传递到log_to_system并让解释器提取为您提供abcd仪表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def log_to_system(a, b, c, d, *args, **kwargs):
    print(a, b, c, d)

def decorator(func):
    def new_func(*args, **kwargs):
        log_to_system(*args, **kwargs)
        return func(*args, **kwargs)
    return new_func

@decorator
def func(a, b, c, d, e):
    pass

func(1, 2, c=3, d=4, e=5)

此输出:

1
1 2 3 4

或者,在将给定的变量参数和关键字参数绑定到修饰函数的签名之后,可以使用inspect.signature获得参数的dict,这样就可以只使用所需的参数调用log_to_system

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import inspect

def log_to_system(a, b, c, d):
    print(a, b, c, d)

def decorator(func):
    sig = inspect.signature(func)
    def new_func(*args, **kwargs):
        arguments = sig.bind(*args, **kwargs).arguments
        log_to_system(**{k: arguments[k] for k in log_to_system.__code__.co_varnames})
        return func(*args, **kwargs)
    return new_func

@decorator
def func(a, b, c, d, e):
    pass

func(1, 2, c=3, d=4, e=5)

此输出:

1
1 2 3 4


您可以使用inspect,工作示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import inspect


def deco(func):
    signature = inspect.signature(func)

    def _deco(*args, **kwargs):
        binded = signature.bind(*args, **kwargs)
        arguments = binded.arguments  # OrderedDict
        print(arguments.items())
        return func(*args, **kwargs)

    return _deco


@deco
def foo(a, b, c, d):
    pass


@deco
def bar(d, c, b, a):
    pass


foo(1, 2, c=3, d=4)  # odict_items([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
bar(1, a=2, b=3, c=4)  # odict_items([('d', 1), ('c', 4), ('b', 3), ('a', 2)])


您可以使用此常规装饰器来转储参数值:

1
2
3
4
5
6
7
8
9
10
11
12
13
def dump_args(func):
    # Decorator to print function call details - parameters names and effective values
    def wrapper(*func_args, **func_kwargs):
        arg_names = func.__code__.co_varnames[:func.__code__.co_argcount]
        args = func_args[:len(arg_names)]
        defaults = func.__defaults__ or ()
        args = args + defaults[len(defaults) - (func.__code__.co_argcount - len(args)):]
        params = list(zip(arg_names, args))
        args = func_args[len(arg_names):]
        if args: params.append(('args', args))
        if func_kwargs: params.append(('kwargs', func_kwargs))
        return func(*func_args, **func_kwargs)
    return wrapper

学分属于@aliteralmind,他在stackoverflow帖子中建议这个装饰师。