Подтвердить что ты не робот

Запись в Python с использованием декоратора

Это первый пример, который мы встречаем, когда сталкиваемся с декораторами. Но я не могу понять, что именно я хотел бы.

Простой декоратор с именем LOG. Он должен работать следующим образом:

@LOG
def f(a, b=2, *c, **d):
    pass

И результат должен выглядеть примерно так:

f(1, pippo=4, paperino='luca')
===== Enter f =====
a = 1
b = 2
pippo = 4
paperino = luca
===== Exit f =====

Где каждый аргумент, переданный как параметр функции, отображается с его значением.

Я обнаружил, что проблема сложнее, чем я думал, в основном из-за множества различных способов передачи аргументов функции (подумайте о кортежах с помощью * c или словарей с ** d).

Я попробовал решение, но я не уверен, что это правильно. Это примерно так:

def LOG(fn):
    import inspect
    varList, _, _, default = inspect.getargspec(fn)
    d = {}
    if default is not None:
        d = dict((varList[-len(default):][i], v) for i, v in enumerate(default))
    def f(*argt, **argd):
        print ('Enter %s' % fn).center(100, '=')
        d.update(dict((varList[i], v) for i, v in enumerate(argt)))
        d.update(argd)
        for c in d.iteritems():
            print '%s = %s' % c
        ret = fn(*argt, **argd)
        print 'return: %s' % ret
        print ('Exit %s' % fn).center(100, '=')
        return ret
    return f

Мне кажется, это не так просто, как я ожидал, но странно, что я не нашел то, что хотел в Google.

Можете ли вы сказать мне, если мое решение в порядке? Или вы можете предложить лучшее решение проблемы, которую я предложил?

Спасибо всем.

4b9b3361

Ответ 1

Единственное, что я заметил, это то, что конструкция dict((varList[i], v) for i, v in enumerate(argt)), которую вы использовали дважды, на самом деле dict(zip(varList,argt)).

Кроме того, у меня есть только метакритичность: ни один из перечисленных выше не принадлежит файлу журнала.

Вместо того, чтобы проходить через журналы до

  • посмотрите, вызываются ли функции с правильными аргументами, которые вы используете для утверждений и отладчика.
  • Посмотрите, вернет ли функция правильные результаты, которые вы пишете unittests.

Ответ 2

В вашей функции все в порядке. Кажется, что вы потерялись с аргументами positional vs variable & keyword.

Позвольте мне объяснить: позиционные аргументы, a и b в вашем случае являются обязательными (и могут иметь значения по умолчанию). Другие аргументы являются необязательными. Если вы хотите сделать аргумент обязательным или иметь значение по умолчанию, поставьте его перед * args и ** kwargs. Но помните, что вы не можете предоставить аргумент дважды:

def x(a = 1, b = 2, *args, **kwargs):
    print a, b, args, kwargs

>>> x(3, 4, 5, b=6)
TypeError: x() got multiple values for keyword argument 'b'

Другой способ, но не такой читаемый, иметь значения по умолчанию для аргументов и не иметь позиционных аргументов:

def x(*args, **kwargs):
    kwargs.updae({'a': 1, 'b': 2})

Ваша функция, анализирующая аргументы, в порядке, хотя я не понимаю, почему вы пишете varargs и keywords в _. Он передает аргументы прозрачно:

def x(a = 1, b = 2, *args, **kwargs):
    print a, b, args, kwargs

def y(*args, **kwargs):
    x(*args, **kwargs)

>>> y(3, 4, 5, 6)
3 4 (5, 6) {}

>>> y(3, 4, 5, b=6)
TypeError: x() got multiple values for keyword argument 'b'

Ответ 3

Я нашел, что ваше хорошее решение может быть немного улучшено, если учесть, что общая функция теоретически может возвращать итерабельность, и в этом случае возникает ошибка.

Вот решение для этого:

Оберните print 'return: %s' % ret в оператор if:
if hasattr(ret, "__iter__"): print 'returned iterable' else: print 'return: %s' % ret

Таким образом, вы не используете много времени для печати больших итераций, но это, конечно, может быть изменено в соответствии с потребностями. (Также строка не имеет атрибута __iter__, что удобно)