为什么Python装饰器不能跨定义链接?

Rob*_*obM 8 python decorator

为什么以下两个脚本不等同?

(取自另一个问题:了解Python装饰器)

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>
Run Code Online (Sandbox Code Playgroud)

并有装饰装饰:

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)
Run Code Online (Sandbox Code Playgroud)

我为什么想知道?我写了一个retry装饰器来捕获MySQLdb异常 - 如果异常是瞬态的(例如Timeout),它会在稍微休眠后重新调用该函数.

我还有一个modifies_db装饰器,负责一些与缓存相关的内务管理.modifies_db装饰着retry,所以我假设装饰的所有功能modifies_db也会隐式重试.我哪里做错了?

Wil*_*ris 9

第二个例子的问题是

@makebold
def makeitalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped
Run Code Online (Sandbox Code Playgroud)

试图装饰makeitalic,装饰器,而不是wrapped它返回的功能.

你可以做我认为你想要的事情:

def makeitalic(fn):
    @makebold
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped
Run Code Online (Sandbox Code Playgroud)

这里makeitalic使用的makebold装饰wrapped.

  • +1这确切地指出了问题以及如何完成它.基本上问题来自思考装饰师的功能组合 - 但他们不这样做. (2认同)