Python装饰器案例如何基础使用:从零到精通的实战指南
目录导读
- 什么是装饰器?——概念与核心原理
- 装饰器的基本写法 —— 函数作为参数与返回值
- 简单装饰器案例 —— 日志、计时、权限检查
- 带参数的装饰器 —— 灵活配置功能
- 类装饰器 vs 函数装饰器 —— 选择依据与案例
- 常见陷阱与最佳实践 —— 使用
functools.wraps - 问答环节 —— 高频问题与解答
- —— 装饰器的精髓与进阶方向
什么是装饰器?——概念与核心原理
核心思想:装饰器本质上是一个 可调用对象(通常是函数或类),它接收一个函数作为输入,并 返回一个新的函数(或修改后的函数),从而在不修改原函数代码的前提下,为其增加额外功能。

为什么需要装饰器?
- 避免重复代码(如日志、鉴权、性能分析)。
- 保持原函数的纯净性(分离核心逻辑与横切关注点)。
小提示:在Python中,装饰器使用 语法糖实现,本质上是
new_func = decorator(old_func)的简写。
装饰器的基本写法 —— 函数作为参数与返回值
# 最简单的装饰器:打印函数名称
def simple_decorator(func):
def wrapper():
print(f"调用函数:{func.__name__}")
return func()
return wrapper
@simple_decorator
def greet():
print("Hello!")
greet()
# 输出:
# 调用函数:greet
# Hello!
执行流程:
greet被定义时,立即传递给simple_decorator。simple_decorator返回wrapper函数,替换原始greet。- 调用
greet()实际调用wrapper()。
简单装饰器案例 —— 日志、计时、权限检查
案例1:日志装饰器(用于调试)
import logging
logging.basicConfig(level=logging.INFO)
def log_call(func):
def wrapper(*args, **kwargs):
logging.info(f"调用 {func.__name__},参数:{args}, {kwargs}")
result = func(*args, **kwargs)
logging.info(f"{func.__name__} 返回:{result}")
return result
return wrapper
@log_call
def add(a, b):
return a + b
add(3, 5) # 日志自动输出调用过程
案例2:计时装饰器(性能分析)
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
print(f"{func.__name__} 耗时:{end-start:.4f}秒")
return result
return wrapper
@timer
def slow_func():
time.sleep(0.5)
return "完成"
slow_func()
案例3:权限检查装饰器
def require_auth(func):
def wrapper(user, *args, **kwargs):
if not user.get("is_authenticated"):
raise PermissionError("用户未认证")
return func(user, *args, **kwargs)
return wrapper
@require_auth
def view_dashboard(user):
return f"欢迎 {user['name']} 访问仪表盘"
# 调用时自动检查权限
带参数的装饰器 —— 灵活配置功能
当装饰器本身需要参数时(如打印不同级别的日志),需要 三层嵌套结构:
def repeat(n=2):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(n=3) # 函数被重复调用3次
def say_hello():
print("Hello!")
say_hello()
原理:@repeat(n=3) 等价于 say_hello = repeat(n=3)(say_hello),即先传入 n 得到装饰器,再用装饰器包装函数。
类装饰器 vs 函数装饰器 —— 选择依据与案例
函数装饰器(推荐用于简单场景)
- 优点:语法直观,性能略高。
- 缺点:状态管理需依赖闭包变量。
类装饰器(适合需要状态管理的复杂场景)
使用 __call__ 方法使类实例可调用:
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"调用次数:{self.count}")
return self.func(*args, **kwargs)
@CountCalls
def hello():
print("Hi!")
hello()
hello()
# 输出调用计数
选择建议:
- 简单无状态 → 函数装饰器。
- 需要保存状态(如缓存、调用计数) → 类装饰器。
常见陷阱与最佳实践 —— 使用 functools.wraps
陷阱:默认装饰器会丢失原函数的元信息(__name__, __doc__ 等)。
@timer
def add(a, b):
"""两个数相加"""
return a + b
print(add.__name__) # 输出 "wrapper" 而非 "add"
解决方案:使用 functools.wraps 复制原函数属性。
from functools import wraps
def timer(func):
@wraps(func) # 将 wrapper 的元信息还原为 func 的
def wrapper(*args, **kwargs):
# 装饰逻辑
pass
return wrapper
其他最佳实践:
- 始终让
wrapper函数支持*args, **kwargs以保证通用性。 - 考虑为装饰器编写单元测试。
问答环节 —— 高频问题与解答
Q1:装饰器可以同时多个吗?执行顺序是怎样的? A:可以,多个装饰器从下到上叠加,执行顺序相反:
@decorator_A @decorator_B def func(): ... # 等价于 func = decorator_A(decorator_B(func)) # 调用时先执行 decorator_A 的外层,再进入 decorator_B
Q2:装饰器可以用于类方法吗?(@classmethod 和普通装饰器结合)
A:可以,注意装饰器的 wrapper 中第一个参数需按方法类型处理(实例方法用 self 或 cls)。
Q3:装饰器能带可选参数吗?(如无参数时默认执行) A:可以,通过判断第一个参数是否为可调用对象:
def repeat(func=None, n=2):
def decorator(f):
# ... 包装逻辑
return wrapper
if func is None:
return decorator
else:
return decorator(func)
# 使用:@repeat 或 @repeat(n=3)
Q4:装饰器性能影响大吗? A:仅增加一次函数调用开销(微秒级),可忽略不计,但装饰器内部若涉及IO或计算,则需注意。
—— 装饰器的精髓与进阶方向
核心要点:
- 装饰器是 高阶函数 的优雅应用,核心在于“包装”而不是“修改”。
- 基础案例:日志、计时、权限、缓存。
- 实践时永远加上
@wraps,保持函数属性完整。
进阶方向:
- 使用
functools.lru_cache实现内存缓存。 - 类装饰器实现单例模式。
- 装饰器结合类型注解做参数验证。
一句话记住:装饰器 = 一句话让函数拥有超能力,且不破坏原函数结构,实际开发中,推荐先从标准库 functools 和 timeit 等内置装饰器学起,逐步自己编写。
文章原创性说明:本文结合了Python官方文档、Real Python、Stack Overflow等来源的核心案例,经过重新组织与详细解释,确保内容精炼且符合SEO结构,如需转载,请保留出处链接。