zhuanlan.zhihu.com/p/127227990…

Python装饰器是一个相对难以理解的概念,Up在初次接触时也觉得晦涩。本文尽可能用通俗易懂的语言和例子,解释清楚Python装饰器。

提前划重点

  • 装饰器为什么难理解
  • 先搞懂闭包
  • 装饰器就是一个语法糖
  • 装饰器的应用场景

装饰器的为什么难理解

我们先来看一个简单的例子,这个例子里我们定义了一个装饰器,用于在greet()函数执行前后各打印一条消息。

# 定义一个简单的装饰器
def simple_decorator(func):     # 传入一个函数作为参数
    # 包装原始函数的内部函数
    def wrapper():
        print("即将执行函数...")
        func()                  # 执行原始函数
        print("函数执行完毕。")
    return wrapper

# 使用装饰器
@simple_decorator
def greet():
    print("你好!")

# 调用被装饰的函数
greet()

-------------------------
## 打印
即将执行函数...
你好!
函数执行完毕。

如果你是第一次接触装饰器,你一定觉得这个这个语法很难理解

  • 定义装饰器的时候,为什么simple_decorator函数里面,居然还有一个wrapper内部函数,并且返回值还是这个wrapper函数???
  • greet函数上面使用@simple_decorator是个什么用法?从来没见过

没关系,大家可以先建立一个印象,大概这个装饰器是怎么用的。下面我们来一一拆解,并没有那么复杂!

先搞懂闭包

理解Python装饰器的前提是,我们先要搞懂闭包这个概念。

通俗来说,闭包就是函数及其周边环境的组合。(在本文语境下,你可以暂时理解周边环境就是一些外部的变量

闭包通常涉及到一个内部函数,内部函数可以访问(或者说‘记住’)外部函数的局部变量即使外部函数已经执行完毕

这么说可能还是有点抽象,我们回到开头那个例子,wrapper就是一个内部函数,当我们调用simple_decorator(greet)会返回wrapper。

def simple_decorator(func): # 传入一个函数作为参数
    def wrapper():
        print("即将执行函数...")
        func()
        print("函数执行完毕。")
    return wrapper # 返回wrapper

def greet():
    print("你好!")

closure = simple_decorator(greet) # simple_decorator(greet)会返回wrapper,这就是一个闭包实例

这个closure就是一个闭包实例!

我们来理解一下,通常我们觉得wrapper这种函数就是它定义的代码吗,很简单:

def wrapper():
    print("即将执行函数...")
    func()
    print("函数执行完毕。")

但是当wrapper作为内部函数的时候,调用simple_decorator(greet)返回的wrapper是一个闭包,除了上面定义的代码,它还能记住外层函数的变量,没错就是func变量,具体到这次调用就是我们传入的greet函数。即使simple_decorator已经执行完了,依然可以记住greet。 这个记住外部变量是大家不太熟悉的或者难以理解的原因。

所以,当我们调用closure()时:

closure()

-------
# 本质是执行了以下代码
print("即将执行函数...")
greet() # greet会被记住
print("函数执行完毕。")

-------
## 打印
即将执行函数...
你好!
函数执行完毕。

装饰器就是一个语法糖

下面我们再说说,@simple_decorator

通过上面的分析相信大家已经看出来了,这两种写法貌似是等效的:

# 写法一
@simple_decorator
def greet():
    print("你好!")

greet() # 调用greet

# 写法二
closure = simple_decorator(greet)
closure() # 调用closure

我们再进一步,其实写法二没必要引入一个新的变量closure,我们完全可以复用greet。

# 写法三
greet = simple_decorator(greet)
greet() # greet

## greet本身指向自己定义的代码
## simple_decorator(greet) 返回wrapper,这是一个闭包
## 直接让greet指向新的闭包
## 这样调用greet()的时候,相当于调用的是闭包

我们抽象一点,@这个用法本质就是一个语法糖,让程序员写代码更方便的一种写法而已:

@decorator
def function():
    ...

------------   
本质上就是
function = decorator(function)

装饰器的应用场景

通过上述分析,我们可以看到,装饰器似乎提供了一种动态修改或者说加强函数的能力,通过使用装饰器,你可以在原函数的基础上增加一些逻辑。

那装饰器主要可以用在哪些场景呢

  1. 日志记录:在函数执行前后自动添加日志记录,方便调试和监控程序行为。
  2. 性能测试:装饰器可以用来测量函数的执行时间,帮助识别性能瓶颈。
  3. 权限验证:在Web开发中,装饰器可以用于检查用户是否有权访问某个资源或执行特定操作。
  4. 缓存(Memoization) :通过装饰器实现函数结果的缓存,避免重复计算相同的输入值,从而提高效率。

等等等等..

当你使用装饰器实现了这些功能,只需要使用@在想要增强的函数前面声明一下,可以非常清晰的表示你在本身函数业务的基础上,增加了什么逻辑。如果没有装饰器,你必须写到函数本身的代码里,这会不利于理解和复用,让这种增强逻辑和本身业务逻辑耦合。所以装饰器,也可以理解成一种优雅的语法,帮助程序变得清晰解耦。

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com