Регистрация Python с использованием декоратора - PullRequest
25 голосов
/ 30 октября 2009

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

Простой декоратор с именем 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.

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

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

Ответы [ 3 ]

5 голосов
/ 30 октября 2009

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

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

Вместо того, чтобы идти по логам к

  • посмотрите, вызваны ли функции с правильными аргументами, которые вы используете, и отладчиком.
  • Посмотрите, возвращает ли функция правильные результаты, которые вы пишете юнит-тесты.
1 голос
/ 30 октября 2009

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

Позвольте мне объяснить: позиционные аргументы, 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'
0 голосов
/ 22 сентября 2016

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

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

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

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

...