我正在和装饰者打交道。按照理论,它们是自下而上应用的,也就是说,最接近被装饰函数的描述的是第一个。然而,代码
'''case 1'''
import functools
def bold(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return '<b>' + func(*args, **kwargs) + '</b>'
return wrapper
def italic(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return '<i>' + func(*args, **kwargs) + '</i>'
return wrapper
strng = '1123123123'
@bold
@italic
def greet(string):
return string
print(greet(strng))
"""Case 2:"""
def dec1(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f'Первый декоратор')
value = func(*args, **kwargs)
return value
return wrapper
def dec2(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f'Второй декоратор')
value = func(*args, **kwargs)
return value
return wrapper
@dec1
@dec2
def function(string):
return string
print(function('fdsfsdf'))
给我结果
<b><i>Hello world!</i></b>
第一个装饰器
第二个装饰器
fdsfsdf
问题是为什么在第一种情况下装饰器像理论上一样使用,首先是斜体,然后是粗体,从下到上。在第二种情况下,首先是 dec1,它在顶部,然后是 dec2,它在下面?我不明白装饰器和函数的描述有何不同,不同行为的原因是什么?
这是幻觉!
案例3:
结论:
那些。:
function('DFFFFFFFFD')包装的,wrapper(*args, **kwargs)顺序运行的代码只需添加更多打印功能,一切就会变得清晰:
结论:
创建装饰器的代码实际上是以相反的顺序调用的。装饰者相互包裹。最底层是要装饰的函数,最后一个装饰器包装了这个函数,倒数第二个装饰器包装了最后一个装饰器,等等。
但是,当整个嵌套娃娃(感谢斯坦尼斯拉夫·沃洛达尔斯基(Stanislav Volodarskiy)提出这个术语)被执行时,它已经从上到下执行了,因为外面有第一个装饰者,他是最后一个包装它的人。这些层以与缠绕方式相反的顺序显露出来。第一个装饰器调用第二个装饰器,依此类推,最后一个装饰器调用被装饰的函数。
嗯,也就是说,这个想法正是装饰器应该按照直接顺序执行,即它们的编写方式。为此,创建装饰器的代码必须以相反的顺序执行,这就是 Python 中的做法。
您提出了一个关于 Python 中装饰器的使用的有趣观点,并且您对两个示例之间行为差异的观察确实需要一些解释。装饰器总是以自下而上的方式应用,这意味着最接近函数的装饰器首先被应用。
让我们更详细地看看您的两个示例:
示例1:
在此示例中,装饰器的应用如下:
italic函数greet,我们得到一个将结果包装greet在 中的函数<i>。bold上一步的结果,将结果包装在 中<b>。因此,结果是:
'<b><i>1123123123</i></b>'。示例2:
在这个例子中,装饰器的顺序是相同的:
dec2函数function,我们得到一个在调用之前打印“Second Decorator”的函数function。dec1上一步的结果,将调用包装在附加的“First Decorator”打印中。所以结果是:
dec1,打印“Firstdecorator”。dec2打印“Second Decorator”。function。结果:
结论
这两个示例都遵循相同的装饰器使用规则:自下而上。之所以出现感知上的差异,是因为在第一个示例中,装饰器修改了函数的返回值,而在第二个示例中,它们在调用函数之前添加了副作用(打印到控制台)。因此,在这两种情况下,装饰器都可以正常工作并以相同的顺序应用,它们只是对最终结果产生不同的影响。