Почему arn't следующие два эквивалентные сценария?
(Взятый от другого вопроса: Понимание Декораторов Python)
def makebold(fn):
def wrapped():
return "" + fn() + ""
return wrapped
def makeitalic(fn):
def wrapped():
return "" + fn() + ""
return wrapped
@makebold
@makeitalic
def hello():
return "hello world"
print hello() ## returns hello world
и с титулованным декоратором:
def makebold(fn):
def wrapped():
return "" + fn() + ""
return wrapped
@makebold
def makeitalic(fn):
def wrapped():
return "" + fn() + ""
return wrapped
@makeitalic
def hello():
return "hello world"
print hello() ## TypeError: wrapped() takes no arguments (1 given)
Почему я хочу знать? Я записал a retry
декоратор для ловли исключений MySQLdb - если исключение является переходным (например, Тайм-аут) оно вспомнит функцию после сна немного.
Я также получил a modifies_db
декоратор, который заботится о некотором связанном с кэшем обслуживании. modifies_db
украшен retry
, таким образом, я предположил, что все функции украсили modifies_db
также повторил бы неявно. Где я шел не так, как надо?
Проблема со вторым примером состоит в том, что
@makebold
def makeitalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
пытается украсить Maseitalic
, декоратор, а не , завернутый
, функция, которая возвращается.
Вы можете делать то, что я думаю, что вы намереваетесь с чем-то вроде этого:
def makeitalic(fn):
@makebold
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
здесь Maseitalic
использует Makebold
, чтобы украсить
.
Причина в том, что обернуты () внутри Мягбальда не принимают никаких аргументов.
Когда вы используете такой декоратор, он может привести к некоторым вопросам, я опубликую пример того, как добиться того, хотя вы хотите, чтобы я даю мне минутку.
Вот рабочий пример того, что вам нужно.
def makebold(rewrap=False):
if rewrap:
def inner(decorator):
def rewrapper(func):
def wrapped(*args, **kwargs):
return "<b>%s</b>" % decorator(func)(*args,**kwargs)
return wrapped
return rewrapper
return inner
else:
def inner(func):
def wrapped(*args, **kwargs):
return "<b>%s</b>" % func(*args, **kwargs)
return wrapped
return inner
@makebold(rewrap=True)
def makeitalic(fn):
def wrapped(*args, **kwargs):
return "<i>%s</i>" % fn(*args, **kwargs)
return wrapped
@makeitalic
def hello():
return "hello world"
@makebold()
def hello2():
return "Bob Dole"
if __name__ == "__main__":
print hello()
print hello2()
MakeBold
вроде уродливая, но он показывает, как написать декоратор, который может при желании обернуть другой декоратор.
Вот выход из вышеуказанного скрипта:
<b><i>hello world</i></b>
<b>Bob Dole</b>
Обратите внимание, что MakeBold является единственным рекурсивным декоратором. Также обратите внимание на тонкую разницу в использовании: @makebold ()
против @makeitalic
.
Проблема заменяет «Макиаталогический» (которая принимает один аргумент) с «обернутой» - в «Мяггильском», который принимает нулевые аргументы.
Использование * args, ** Kwargs
, чтобы пройти на аргументы дальше вниз по цепочке:
def wrapped(*args, **kwargs):
return "<b>" + fn(*args, **kwargs) + "</b>"