代码之家  ›  专栏  ›  技术社区  ›  RobM

为什么不能跨定义链接Python修饰符?

  •  8
  • RobM  · 技术社区  · 15 年前

    为什么下面的两个脚本不是等价的?

    (摘自另一个问题: 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 decorator捕获mysqldb异常-如果异常是暂时的(例如超时),它将在休眠一段时间后重新调用函数。

    我也有一个 modifies_db 负责一些与缓存相关的内务管理的decorator。 修改数据库 装饰有 重试 ,所以我假设所有的函数都用 修改数据库 也会隐式重试。我哪里出错了?

    3 回复  |  直到 15 年前
        1
  •  9
  •   Will Harris    15 年前

    第二个例子的问题是

    @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
    

    在这里 补语的 使用 makebold 装饰 包裹 .

        2
  •  1
  •   Bryan McLemore    15 年前

    原因是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() VS @makeitalic .

        3
  •  0
  •   mthurlin    15 年前

    问题是将“makeitalic”(接受一个参数)替换为“makebold”中的“wrapped”-函数,该函数接受零个参数。

    使用 *args, **kwargs 要进一步传递参数,请执行以下操作:

    def wrapped(*args, **kwargs):
        return "<b>" + fn(*args, **kwargs) + "</b>"