CATS
138.50M · 2026-02-04
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()
-------------------------
## 打印
即将执行函数...
你好!
函数执行完毕。
如果你是第一次接触装饰器,你一定觉得这个这个语法很难理解
没关系,大家可以先建立一个印象,大概这个装饰器是怎么用的。下面我们来一一拆解,并没有那么复杂!
理解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)
通过上述分析,我们可以看到,装饰器似乎提供了一种动态修改或者说加强函数的能力,通过使用装饰器,你可以在原函数的基础上增加一些逻辑。
那装饰器主要可以用在哪些场景呢
等等等等..
当你使用装饰器实现了这些功能,只需要使用@在想要增强的函数前面声明一下,可以非常清晰的表示你在本身函数业务的基础上,增加了什么逻辑。如果没有装饰器,你必须写到函数本身的代码里,这会不利于理解和复用,让这种增强逻辑和本身业务逻辑耦合。所以装饰器,也可以理解成一种优雅的语法,帮助程序变得清晰解耦。