【问题标题】:Python decorator examplePython 装饰器示例
【发布时间】:2016-07-25 10:53:35
【问题描述】:

我从thecodeship 上的一篇很棒的教程中学到了一些关于装饰器的知识,但发现自己被一个例子弄糊涂了。

首先给出一个简单的例子,然后解释什么是装饰器。

def p_decorate(func):
   def func_wrapper(name):
       return "<p>{0}</p>".format(func(name))
   return func_wrapper

def get_text(name):
   return "lorem ipsum, {0} dolor sit amet".format(name)

my_get_text = p_decorate(get_text)

print my_get_text("John") 

现在这对我来说很有意义。装饰器只是函数的包装器。在这个家伙的解释中,他说装饰器是一个函数,它接受另一个函数作为参数,生成一个新函数,然后返回生成的函数以在任何地方使用。

现在等价于上面的代码是:

def p_decorate(func):
   def func_wrapper(name):
       return "<p>{0}</p>".format(func(name))
   return func_wrapper

@p_decorate
def get_text(name):
   return "lorem ipsum, {0} dolor sit amet".format(name)

print get_text("John")

我相信我理解在没有参数时初始化装饰器的方式。如果我错了,请纠正我。

  • 装饰器默认传入函数get_text,因为p_decorate返回函数func_wrapper,我们最终得到了正确的声明get_text = func_wrapper

对我来说重要的是第一个代码块等价物,因为我看到并理解装饰器的行为方式。

让我非常困惑的是以下代码:

def tags(tag_name):
    def tags_decorator(func):
        def func_wrapper(name):
            return "<{0}>{1}</{0}>".format(tag_name, func(name))
        return func_wrapper
    return tags_decorator

@tags("p")
def get_text(name):
    return "Hello "+name

print get_text("John")

再次,如果我错了,请纠正我,但这是我的理解。

  • 装饰器接受标签字符串“p”而不是 默认函数名。反过来函数tags_decorator 假设将要传入的参数是函数 正在装修,get_text

以“非装饰器”形式查看等效的代码块可能对我有所帮助,但我似乎无法理解它的外观。我也不明白为什么tags_decoratorfunc_wrapper 都被返回了。如果装饰器只需要返回1个函数来包装get_text,那么返回两个不同函数的目的是什么。

顺便说一句,实际上归结为以下几点。

  • 能否将此块简化为少于 3 个函数的集合?
  • 装饰器可以接受多个参数来简化代码吗?

【问题讨论】:

    标签: python decorator python-decorators


    【解决方案1】:

    在限制范围内,@ 之后的所有内容都会被执行以生成一个装饰器。在您的第一个示例中,@ 之后的内容只是一个名称:

    @p_decorate
    

    所以 Python 查找 p_decorate 并使用修饰函数作为参数调用它:

    get_text = p_decorate(get_text)
    

    (有点过于简单了,get_text 最初并未绑定到原始函数,但您已经掌握了要点)。

    在您的第二个示例中,装饰器表达式涉及更多内容,它包括一个调用:

    @tags("p")
    

    所以 Python 使用 tags("p") 来查找装饰器。最后这是执行的:

    get_text = tags("p")(get_text)
    

    tags("p")输出就是这里的装饰器!我将tags 函数本身称为装饰器factory,它在调用时会产生一个装饰器。当您调用tags() 时,它会返回tags_decorator()。这就是 真正的 装饰器。

    您可以改为删除装饰器函数并硬编码 "p" 值并直接使用它:

    def tags_decorator_p(func):
        def func_wrapper(name):
            return "<{0}>{1}</{0}>".format("p", func(name))
        return func_wrapper
    
    @tags_decorator_p
    def get_text(name):
        # ...
    

    但是您必须为tags() 的参数的每个可能值创建单独的装饰器。这就是装饰器工厂的价值,您可以向装饰器添加参数并更改函数的装饰方式。

    装饰器 factory 可以接受任意数量的参数;它只是您调用以生成装饰器的函数。装饰器本身只能接受一个参数,即要装饰的函数。

    我在回答开始时说,在限制内是有原因的; @ 后面的表达式的语法只允许使用带点的名称(foo.bar.baz,因此属性访问)和调用((arg1, arg2, keyword=arg3))。请参阅reference documentation。原PEP 318 状态:

    装饰器语句在它可以接受的范围内是有限的——任意表达式都不起作用。 Guido 更喜欢这个,因为直觉 [17]。

    【讨论】:

    • 谢谢。在您回复后,我做了一个简短的编辑。为什么这些函数集返回 2 个函数而不是 1 个?
    • @Max:查看我的最新编辑。一个是装饰器(装饰器工厂生产),另一个是替换原函数的包装器,装饰器的结果。
    • 我现在明白了,这两个函数调用的目的。我可以使用装饰器工厂制作特定的装饰器,以进一步指定我的最终包装器。
    猜你喜欢
    • 1970-01-01
    • 2013-12-06
    • 1970-01-01
    • 2021-10-10
    • 2016-07-27
    • 1970-01-01
    • 2013-02-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多