为什么以下两个脚本不等同?
(取自另一个问题:Understanding Python Decorators)
def makebold(fn):
def wrapped():
return "<b>" + fn() + "</b>"
return wrapped
def makeitalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
@makebold
@makeitalic
def hello():
return "hello world"
print hello() ## returns <b><i>hello world</i></b>
并带有装饰装饰:
def makebold(fn):
def wrapped():
return "<b>" + fn() + "</b>"
return wrapped
@makebold
def makeitalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
@makeitalic
def hello():
return "hello world"
print hello() ## TypeError: wrapped() takes no arguments (1 given)
为什么我想知道?我写了一个retry
装饰器来捕获MySQLdb异常 - 如果异常是暂时的(例如Timeout),它会在稍微休眠后重新调用该函数。
我还有一个modifies_db
装饰器,它负责一些与缓存相关的内务处理。 modifies_db
装饰有retry
,因此我假设所有用modifies_db
修饰的函数也会隐式重试。我哪里出错了?
答案 0 :(得分:9)
第二个例子的问题是
@makebold
def makeitalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
正在尝试装饰装饰器makeitalic
,而不是wrapped
,它返回的功能。
你可以做我认为你想要的事情:
def makeitalic(fn):
@makebold
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
此处makeitalic
使用makebold
来装饰wrapped
。
答案 1 :(得分:1)
原因是因为makebold中的wrapped()不接受任何参数。
当你像这样使用装饰器时会引起一些问题,我会发布一个如何实现你想要的东西的例子,给我一点时间。
以下是您需要的实例。
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
。
答案 2 :(得分:0)
问题是将“makeitalic”(带一个参数)替换为“makebold”中的“wrapped”函数,该函数接受零参数。
使用*args, **kwargs
在链中进一步传递参数:
def wrapped(*args, **kwargs):
return "<b>" + fn(*args, **kwargs) + "</b>"