Давайте предположим, что я получил следующую функцию:

def do_something(arg_1, arg_2, arg_3):
    logger.info('arg_1: {0} arg_2: {1} arg_3: {2}'.format(arg_1, arg_2, arg_3))
    print('Doing something..')

Есть ли элегантный способ записать все аргументы функции без перечисления имен аргументов?

0
wmatt 17 Сен 2018 в 08:30

2 ответа

Лучший ответ

Я считаю, что сейчас самое время заняться декораторами. Посмотрите на следующее:

def log_args(function):

    @functools.wraps(function)
    def wrapper(*args, **kwargs):
        logger.info(', '.join(str(x) for x in args))
        return function(*args, **kwargs)

    return wrapper

Строка @functools.wraps является необязательной

Теперь украсьте ваши функции так:

@log_args
def do_something(arg1, arg2):
    print('doing something..')

Объяснение: строка @log_args такая же, как и в случае:

do_something = log_args(do_something)

Теперь этот log_args берет функцию и входит в локальную область видимости. Внутри этой области определена другая функция, называемая оболочкой. Эта функция содержит регистратор, который форматирует аргументы в строку. Затем результат функции возвращается из оболочки. Чтобы убедиться, что do_something по-прежнему является функцией, я возвращаю wrapper. Теперь, когда вызывается do_something, фактически вызывается обертка, и происходит описанный выше процесс.

Причина, по которой я использовал декоратор, заключается в том, что я могу сохранить аргументы явных имен в определении функции, однако внутри декоратора я могу использовать *args, который упаковывает эти аргументы в список, даже если сама функция имеет только арг . Примечание. Я не уверен на 100% относительно последнего пункта о сохранении аргументов. На самом деле, я не думаю, что это вообще так.

3
N Chauhan 17 Сен 2018 в 05:55

Чтобы расширить ответ @ NChauhan, если вы хотите иметь возможность регистрировать не только значения позиционных аргументов, но и имена позиционных аргументов и аргументов ключевых слов вместе с их заданными значениями и значениями по умолчанию, чтобы:

@log_args
def do_something(arg_1, arg_2, arg_3='world'):
    pass

do_something(2, arg_2='hello')

Может вывести:

do_something called with arg_1: 2, arg_2: 'hello', arg_3: 'world'

Вы можете использовать inspect.signature с вашим декоратором:

import inspect
from functools import wraps
def log_args(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        bound = sig.bind(*args, **kwargs)
        bound.apply_defaults()
        print('{} called with {}'.format(func.__name__, ', '.join('{}: {}'.format(name, repr(value)) for name, value in bound.arguments.items())))
        return func(*args, **kwargs)
    sig = inspect.signature(func)
    return wrapper
1
blhsing 17 Сен 2018 в 08:58