Python 装饰器详解:优雅地给代码“穿外衣”
在 Python 的世界里,你一定见过那个神奇的 @ 符号。它悬浮在函数定义的上方,像是一个神秘的开关。这就是 装饰器 (Decorator)。
很多初学者觉得装饰器难以理解,甚至有点“魔法”的感觉。但实际上,一旦你理解了它的核心逻辑,它将成为你编写高质量、高可维护性 Python 代码的得力助手。
本文将深入探讨装饰器的作用,以及它如何帮助我们写出更优雅的代码。
- 什么是装饰器?
用一句话概括:装饰器本质上是一个 Python 函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能。
这就好比你有一幅画(原函数),你觉得它太单调,想给它加个相框(装饰器)。你不需要修改画的内容,只需要把它放进相框里,它就拥有了展示、保护等新的“功能”。
- 核心作用:为什么要用装饰器?
装饰器的核心价值在于遵循了软件设计中的 “开放-封闭原则” (Open-Closed Principle):
对扩展开放:允许增加新功能。
对修改关闭:不需要修改原函数的源代码。
试想一下,如果你有 50 个函数,你需要统计它们每一个的执行时间。
笨办法:修改这 50 个函数,在每个函数开头和结尾加计时代码。这不仅累,而且如果以后逻辑变了,你还得再改 50 次。
装饰器办法:写一个“计时装饰器”,只需要在这是 50 个函数头上加一个 @timer 标签即可。
- 从零开始:一个最简单的装饰器
让我们看一个最基础的例子。假设我们有一个简单的函数:
def say_hello():
print("Hello, World!")
我们想在它说话之前先打个招呼,说完后再说再见。
定义装饰器
def my_decorator(func):
def wrapper():
print("--- wrapper: 在原函数执行前做点什么 ---")
func() # 执行原函数
print("--- wrapper: 在原函数执行后做点什么 ---")
return wrapper
使用装饰器
@my_decorator
def say_hello():
print("Hello, World!")
调用
say_hello()
输出结果:
--- wrapper: 在原函数执行前做点什么 ---
Hello, World!
--- wrapper: 在原函数执行后做点什么 ---
你看,我们没有修改 say_hello 内部的一行代码,就改变了它的行为。
- 装饰器的三大实战场景
理解了原理,我们来看看在实际工作中,装饰器最常用于哪些场景。
场景一:日志记录 (Logging)
这是最常见的用途。我们希望在函数被调用时,自动记录函数名和参数,方便调试。
import functools
def logger(func):
@functools.wraps(func) # 这是一个好习惯,保留原函数的元数据
def wrapper(args, **kwargs):
print(f"LOG: 正在调用函数 {func.name},参数为 {args} {kwargs}")
result = func(args, **kwargs)
print(f"LOG: 函数 {func.name} 调用结束")
return result
return wrapper
@logger
def add(x, y):
return x + y
add(3, 5)
场景二:性能测试 (Timing)
检查代码运行慢在哪里,是优化的第一步。
import time
def timer(func):
def wrapper(args, **kwargs):
start_time = time.time()
result = func(args, **kwargs)
end_time = time.time()
print(f"函数 {func.name} 执行耗时: {end_time - start_time:.4f} 秒")
return result
return wrapper
@timer
def heavy_computation():
time.sleep(1) # 模拟耗时操作
heavy_computation()
输出: 函数 heavy_computation 执行耗时: 1.0012 秒
场景三:权限验证 / 身份认证 (Authentication)
在 Web 开发(如 Flask 或 Django)中,装饰器被大量用于检查用户是否登录。
current_user = None # 模拟当前没有用户登录
def login_required(func):
def wrapper(args, **kwargs):
if not current_user:
print("错误: 用户未登录,无法执行操作!")
return None
return func(args, **kwargs)
return wrapper
@login_required
def view_profile():
print("正在查看用户个人资料...")
尝试调用
view_profile()
输出: 错误: 用户未登录,无法执行操作!
- 一个关键细节:functools.wraps
细心的你可能注意到了上面代码中出现了 @functools.wraps(func)。这是做什么的?
当你使用装饰器后,原函数其实已经被 wrapper 函数替代了。这意味着原函数的 name(函数名)和 doc(文档字符串)都会变成 wrapper 的。这会导致调试困难。
@functools.wraps(func) 的作用就是把原函数的元数据“复制”到 wrapper 上,这是一种编写装饰器的最佳实践。
- 总结
装饰器是 Python 中非常强大且优雅的特性。
作用:在不修改原函数代码的情况下,动态地给函数增加功能。
优点:代码复用率高,逻辑清晰,符合“开放-封闭原则”。
应用:日志、计时、缓存、权限校验等横切面关注点(Cross-cutting concerns)。
掌握了装饰器,你就掌握了 Python 高效编程的一把金钥匙。

浙公网安备 33010602011771号