Python中经过验证的函数参数

时间:2019-02-22 19:03:10

标签: python python-decorators

我有一个功能

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

我正在尝试编写一个装饰器,该装饰器可以理解这些参数并将其中一些参数记录到另一个系统中。

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中提取参数值的库或实用程序?

3 个答案:

答案 0 :(得分:3)

一种简单的方法是使您的log_to_system函数除了实际记录的已知参数之外,还接受变量参数和变量关键字参数,以便您可以简单地从以下位置传递变量参数和变量关键字参数:装饰的功能log_to_system,让解释器为您提取参数abcd

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 2 3 4

或者,您可以在将给定的变量参数和关键字参数绑定到装饰函数的签名之后,使用inspect.signature来获取参数的字典,这样您就可以仅使用所需的参数来调用log_to_system

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 2 3 4

答案 1 :(得分:1)

您可以使用inspect(工作示例)

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)])

答案 2 :(得分:0)

您可以使用以下通用修饰符来转储参数值:

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 post中建议使用此装饰器。