Как объяснили другие ответы, декораторам обычно передается один неявный аргумент функции, когда они вызываются, просто используя свое имя следующим образом:
@deco
def somefunc(...): pass
Что делает то же самое, что и:
def somefunc(...): pass
somefunc = deco(somefunc)
Аргумент в этой ситуации представляет собой скомпилированную версию определения функции, которая следует сразу после. В таких случаях декоратор возвращает вызываемый объект, который присваивается имени функции вместо тела скомпилированной функции, как это обычно бывает.
Однако, если функции-декоратору при вызове явно дается один или несколько аргументов, например:
@deco(args)
def somefunc(...): pass
Становится эквивалентным:
def somefunc(...): pass
somefunc = deco(args)(somefunc)
Как видите, вещи в этом случае работают несколько иначе. Функция декоратора по-прежнему возвращает вызываемый объект, только на этот раз , который ожидается для «нормальной» функции декоратора с неявным аргументом, которая затем вызывается с объектом функции из следующего определения функции, как и раньше. *
Снова & ndash; как уже отмечали другие - & ndash; это делает декораторам явно переданные аргументы, фабрики декораторов в том смысле, что они конструируют и возвращают «обычные» функции декоратора.
В большинстве, если не во всех случаях, декораторы могут быть реализованы либо как функция, либо как класс, так как оба они могут вызываться в Python. Лично я нахожу функции немного более понятными и поэтому буду использовать этот подход в следующем. С другой стороны, функциональный подход может стать хитрым, поскольку он часто включает одно или несколько определений вложенных функций.
Вот как вы можете кодировать декоратор для функции do()
в вашем модуле. В приведенном ниже коде я определил, что он делает - выводит аргументы функции перед ее вызовом.
def do(fn, args=tuple(), kwargs={}, priority=0,
block=False, timeout=0, callback=None, daemon=False):
# show arguments
print ('in do(): fn={!r}, args={}, kwargs={}, priority={},\n'
' block={}, timeout={}, callback={}, daemon={}'
.format(fn.__name__, args, kwargs, priority,
block, timeout, callback, daemon))
# and call function 'fn' with its arguments
print (' calling {}({}, {})'.format(
fn.__name__,
', '.join(map(str, args)) if args else '',
', '.join('{}={}'.format(k, v) for k,v in kwargs.items())
if kwargs else '')
)
fn(*args, **kwargs)
def do_decorator(**do_kwargs):
def decorator(fn):
def decorated(*args, **kwargs):
do(fn, args, kwargs, **do_kwargs)
return decorated
return decorator
@do_decorator(priority=2)
def decoratedTask(arg, dic=42):
print 'in decoratedTask(): arg={}, dic={}'.format(arg, dic)
decoratedTask(72, dic=3)
Выход:
in do(): fn='decoratedTask', args=(72,), kwargs={'dic': 42}, priority=2,
block=False, timeout=0, callback=None, daemon=False
calling decoratedTask(72, dic=3)
in decoratedTask(): arg=72, dic=3
Вот краткий отчет о том, как работает этот сложный на вид материал:
Функция внешнего декоратора do_decorator()
, определяет другую функцию внутреннего декоратора, которую она возвращает, здесь с креативным названием decorator
.
Что делает decorator
, так это определяет, что происходит с функциями, которые оформлены в простом сценарии «без аргументов» - здесь определяется и возвращается еще один & ndash; но окончательно - вложенная функция с именем decorated
, которая просто вызывает функцию do()
модуля и передает ей оба аргумента, если таковые имеются, с точки вызова, наряду с аргументами, предназначенными для использования функции do()
.
Этот вариант использования несколько усложняется тем, что как внешний декоратор, так и декорируемые функции имеют аргументы с ключевыми словами. Требуется особая осторожность, чтобы гарантировать, что имена ключевых слов для каждого уникальны, чтобы они не конфликтовали (и чтобы значение изменяемого аргумента kwargs
по умолчанию не было случайно изменено чем-то в функции do()
).