你写 Python 时有没有见过这种语法,函数上面顶着一个 @something,不知道是什么意思?
这就是 Python 的装饰器(Decorator),Flask、Django、FastAPI 等主流框架里随处可见它的身影。
很多人觉得装饰器神秘难懂,其实原理超级简单。今天用 10 行核心代码,带你彻底搞明白它。
前置知识
Python 函数也是"对象"
要理解装饰器,先要知道一件事:Python 里函数也是对象,可以像变量一样传来传去,也可以作为参数传给另一个函数。
🐍 函数作为变量
def say_hello():
print("Hello!")
# 函数可以赋值给变量
greet = say_hello
greet() # 输出:Hello!
# 函数可以作为参数传入另一个函数
def run(func):
func()
run(say_hello) # 输出:Hello!
💡 理解了"函数是对象",装饰器就自然明白了。
原理
装饰器到底是什么?
装饰器就是一个接收函数、返回新函数的函数。它能在不修改原函数代码的情况下,给函数"套一层外衣",添加新功能。
🔧 手动实现一个计时装饰器
import time
# 这就是一个装饰器函数
def timer(func): # ① 接收一个函数
def wrapper():
start = time.time()
func() # ② 调用原函数
end = time.time()
print(f"耗时:{end-start:.3f}s")
return wrapper # ③ 返回新函数
# 手动使用:把函数"包裹"起来
def slow_task():
time.sleep(1)
slow_task = timer(slow_task) # 手动包裹
slow_task() # 输出:耗时:1.001s
✅ 看清楚了吗?装饰器就是把原函数"塞进"一个新函数里,新函数在调用原函数的前后可以做额外的事情。
语法糖
@ 符号让写法更优雅
slow_task = timer(slow_task) 这种写法太繁琐。Python 提供了 @ 语法糖,两行变一行:
✨ 使用 @ 语法糖(效果完全一样)
@timer # 等同于 slow_task = timer(slow_task)
def slow_task():
time.sleep(1)
slow_task() # 输出:耗时:1.001s
❌ 不用 @ 的写法
def slow_task():
...
slow_task = timer(slow_task)
✅ 用 @ 的写法
@timer
def slow_task():
...
💡 @ 只是语法糖,背后做的事情完全一样。理解了原理,就不会再对 @ 感到神秘!
进阶
让装饰器支持有参数的函数
如果被装饰的函数有参数,需要在 wrapper 里用 *args, **kwargs 接收并传递:
🔧 通用计时装饰器(支持任意参数)
def timer(func):
def wrapper(*args, **kwargs): # 接收任意参数
start = time.time()
result = func(*args, **kwargs) # 传递参数
print(f"{func.__name__} 耗时:{time.time()-start:.3f}s")
return result # 保留返回值
return wrapper
@timer
def add(a, b):
return a + b
print(add(3, 5))
# add 耗时:0.000s
# 8
实战
3 个超实用装饰器场景
🔐 场景一:登录状态校验
def login_required(func):
def wrapper(*args, **kwargs):
if not is_logged_in():
return "请先登录"
return func(*args, **kwargs)
return wrapper
@login_required
def view_profile():
return "个人主页内容"
🔄 场景二:自动重试(网络请求失败时)
def retry(times=3): # 带参数的装饰器
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(times):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"第{i+1}次失败:{e}")
return wrapper
return decorator
@retry(times=3) # 失败自动重试3次
def fetch_data(url):
# 发起网络请求...
⚡ 场景三:用 Python 内置的缓存装饰器
from functools import lru_cache
@lru_cache(maxsize=128) # 自动缓存计算结果
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 重复调用直接读缓存,速度飞快
print(fibonacci(35)) # 9227465
💡 lru_cache 是 Python 标准库自带的缓存装饰器,递归函数加上它性能提升几十倍!
📋 装饰器知识速查
概念
说明
装饰器本质
接收函数、返回新函数的函数
@ 符号
语法糖,等同于 f = dec(f)
*args **kwargs
让装饰器适配任意参数的函数
functools.wraps
保留原函数名称和文档
lru_cache
内置缓存装饰器,提速利器
叠加装饰器
多个 @ 从下往上依次包裹
🛠️ 动手挑战
试着自己写一个日志装饰器,要求:
① 在函数执行前打印:正在调用 xxx 函数...
② 在函数执行后打印:xxx 函数执行完毕
③ 支持任意参数的函数
🎯 提示:用 func.__name__ 可以获取函数名。写好后在评论区分享你的代码吧!
— 下期预告 —
用 Python 做一个简单的聊天机器人
调用免费 AI 接口,30 行代码实现命令行对话
——敬请期待
奥饼饼智慧谷 · 每天一点编程趣味
❤️ 如果对你有帮助,欢迎转发给身边学编程的朋友~