古明地觉的编程教室 2024年11月28日
装饰器是怎么实现的?
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

Python装饰器本质上是高阶函数和闭包的结合,提供了一种优雅的语法糖,可以在不修改原函数代码和调用方式的情况下,为函数添加新功能。文章深入浅出地讲解了装饰器的原理,包括装饰器的工作机制、语法糖的本质、闭包的作用以及多个装饰器的执行顺序。通过示例代码,阐述了装饰器如何为函数添加前置和后置操作,并解释了@符号的语法糖作用。此外,文章还提到了functools模块中的wraps函数,用于保留原始函数信息。最后,文章强调理解闭包是理解装饰器的关键,并总结了装饰器的执行顺序是从下往上装饰,从上往下执行。

🤔装饰器本质上是高阶函数和闭包的结合,提供了一种简洁的语法糖,允许在不修改原函数代码和调用方式的前提下,为函数添加额外的功能。例如,在函数执行前后添加日志打印、计时等操作。

🚀装饰器语法糖@符号等价于直接调用装饰器函数,例如`foo = deco(foo)`,理解装饰器的关键在于理解闭包的概念。

🔄多个装饰器时,装饰顺序是从下往上,执行顺序是从上往下。例如,`@deco1@deco2@deco3` 会先装饰`deco3`,再装饰`deco2`,最后装饰`deco1`,执行时则按照`deco1`、`deco2`、`deco3`的顺序执行。

📦为了保留原始函数信息,可以使用`functools`模块中的`wraps`函数,避免函数信息丢失。

💡理解闭包是理解装饰器的关键,因为装饰器本质上就是利用闭包来实现功能增强。

原创 古明地觉 2024-11-28 10:00 北京

装饰器是 Python 的一个亮点,但并不神秘,因为它本质上就是高阶函数加上闭包,只不过给我们提供了一个优雅的语法糖。

至于为什么要有装饰器,我觉得有句话说的非常好,装饰器存在的最大意义就是可以在不改动原函数的代码和调用方式的情况下,为函数增加一些新的功能。

def deco(func):
    print("都闪开,我要开始装饰了")
    def inner(*args, **kwargs):
        print("开始了")
        ret = func(*args, **kwargs)
        print("结束")
        return ret
    return inner
# 这一步等价于 foo = deco(foo)
# 因此上来就会打印 deco 里面的 print
@deco
def foo(a, b):
    print(f"a = {a},b = {b}")
print("---------")
"""
都闪开,我要开始装饰了
---------
"""

# 此时再调用 foo,已经不再是原来的 foo 了
# 而是 deco 里面的闭包 inner
foo(12)
"""
开始了
a = 1,b = 2
结束
"""

如果不使用装饰器的话:

def deco(func):
    print("都闪开,我要开始装饰了")
    def inner(*args, **kwargs):
        print("开始了")
        ret = func(*args, **kwargs)
        print("结束")
        return ret
    return inner
def foo(a, b):
    print(f"a = {a},b = {b}")
foo = deco(foo)
"""
都闪开,我要开始装饰了
"""

foo(12)
"""
开始了
a = 1,b = 2
结束
"""

打印结果告诉我们,装饰器只是类似于 foo=deco(foo) 的一个语法糖罢了。

至于字节码这里就不看了,还是那句话,只是个语法糖,它和我们直接调用 foo=deco(foo) 是等价的,所以理解装饰器(decorator)的关键就在于理解闭包(closure)。

另外函数在被装饰器装饰之后,整个函数其实就已经变了,而为了保留原始信息我们一般会从 functools 模块中导入一个 wraps 函数。当然装饰器还可以写的更复杂,比如带参数的装饰器、类装饰器等等,不过这些都属于 Python 层级的东西了,我们就不说了。

另外装饰器还可以不止一个,如果一个函数被多个装饰器装饰,会有什么表现呢?

def deco1(func):
    def inner():
        return f"<deco1>{func()}</deco1>"
    return inner
def deco2(func):
    def inner():
        return f"<deco2>{func()}</deco2>"
    return inner
def deco3(func):
    def inner():
        return f"<deco3>{func()}</deco3>"
    return inner
@deco1
@deco2
@deco3
def foo():
    return "古明地觉"
print(foo())

解释器还是从上到下解释,当执行到 @deco1 的时候,肯定要装饰了,但它下面不是函数,也是一个装饰器,于是表示:要不哥们,你先装饰。然后执行 @deco2,但它下面还是一个装饰器,于是重复了刚才的话,把皮球踢给 @deco3


当执行 @deco3 的时候,发现下面终于是一个普通的函数了,于是装饰了。


deco3 装饰完毕之后,foo = deco3(foo)。然后 deco2 发现 deco3 已经装饰完毕,那么会对 deco3 装饰的结果再进行装饰,此时 foo = deco2(deco3(foo));同理,再经过 deco1 的装饰,最终得到了 foo  =  deco1(deco2(deco3(foo)))


于是最终输出:

<deco1><deco2><deco3>古明地觉</deco3></deco2></deco1>

所以当有多个装饰器的时候,会从下往上装饰;然后执行的时候,会从上往下执行。

以上就是装饰器相关的内容,可以说非常简单了,甚至有点水文章的嫌疑,因为核心都在上一篇介绍的闭包当中。还是那句话,理解装饰器的关键就在于理解闭包。

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Python 装饰器 闭包 高阶函数 语法糖
相关文章