Проверенные аргументы функций в Python - PullRequest
0 голосов
/ 22 февраля 2019

У меня есть функция

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

Проблема в том, чточто у декоратора нет простого способа извлечь значения a, b, c, d как из аргументов arg, так и из kwargs, поскольку пользователь может передать их, используя аргументы позиционирования или ключевые слова.Я также хотел бы сохранить этот универсальный, поскольку этот декоратор может использоваться в различных функциях.

Есть ли библиотека или утилита, которая может легко извлечь значения параметров из аргументов arg и kwargs?

Ответы [ 3 ]

0 голосов
/ 22 февраля 2019

Вы можете использовать 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)])
0 голосов
/ 22 февраля 2019

Вы можете использовать этот общий декоратор для вывода значений параметров:

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 .

0 голосов
/ 22 февраля 2019

Простой подход заключается в том, чтобы ваша log_to_system функция принимала переменные параметры и переменные ключевые слова в дополнение к известным параметрам, которые она будет фактически регистрировать, так что вы можете просто передать переменные аргументы и переменные ключевые слова аргументы из декорированногоназначить log_to_system и позволить интерпретатору извлечь параметры a, b, c и d для вас:

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
...