我正在从关于 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_decorator
和 func_wrapper
都被返回。如果装饰器只需要返回 1 个函数来包装 get_text
,那么返回两个不同的函数的目的是什么。
作为旁注,它实际上归结为以下内容。
- 能否将此 block 简化为少于 3 个函数的集合?
- 装饰器可以接受超过 1 个参数来简化代码吗?
最佳答案
在限制范围内,执行 @
之后的所有内容以生成 装饰器。在您的第一个示例中, @
之后的只是一个名称:
@p_decorate
因此 Python 查找 p_decorate
并使用装饰函数作为参数调用它:
get_text = p_decorate(get_text)
(有点过分简化了,get_text
最初并未绑定(bind)到原始函数,但您已经掌握了要点)。
在你的第二个例子中,装饰器表达式有点复杂,它包括一个调用:
@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()
的参数的每个可能值创建单独的装饰器。这就是装饰器工厂的值(value),您可以向装饰器添加参数并更改函数的装饰方式。
装饰器工厂 可以接受任意数量的参数;它只是您调用以生成装饰器的函数。装饰器自身只能接受一个参数,即要装饰的函数。
我在回答开始时说,在限制范围内是有原因的; @
后面的表达式的语法只允许一个带点的名称(foo.bar.baz
,so 属性访问)和一个调用((arg1,arg2,关键字=arg3)
)。查看reference documentation .原文PEP 318状态:
The decorator statement is limited in what it can accept -- arbitrary expressions will not work. Guido preferred this because of a gut feeling [17] .
关于Python装饰器示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36427374/