一、函数是可传递的"乐高积木"
Python函数天生具备两种特殊能力:可以作为参数传递,也能作为返回值返回。这种特性让函数像乐高积木般灵活,能够自由组合成更复杂的结构。
def greet(name):
return f"Hello, {name}"
def call_func(func, arg):
return func(arg)
print(call_func(greet, "Alice")) # 输出:Hello, Alice
当call_func接收greet作为参数时,本质上是在传递函数的行为而非结果。这种"行为传递"的特性,为装饰器的实现奠定了基础。
二、装饰器的本质:函数包装术
装饰器本质上是接受函数作为输入,返回新函数的"函数工厂"。其核心模式可以用三明治比喻理解:
def decorator(func):
def wrapper():
print("准备食材")
result = func()
print("装盘上桌")
return result
return wrapper
@decorator
def make_sandwich():
print("夹肉饼")
make_sandwich()
当@decorator修饰make_sandwich时,实际发生的是:
- 原函数make_sandwich被传递给decorator
- decorator返回的wrapper函数替代原函数
- 调用新函数时,会先执行准备操作,再执行原函数,最后完成收尾工作
这种包装模式实现了在不修改原函数代码的前提下,为其添加额外功能。
三、装饰器的三大应用场景
1. 横切关注点分离
日志记录、性能监控等通用功能可以抽离为装饰器:
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f"耗时:{time.time()-start:.4f}秒")
return result
return wrapper
@timer
def process_data(data):
time.sleep(0.5)
return [x*2 for x in data]
process_data([1,2,3]) # 输出包含耗时信息
2. 权限校验中枢
通过装饰器统一处理用户认证逻辑:
def auth_required(role):
def decorator(func):
def wrapper(user, *args, **kwargs):
if user.role != role:
raise PermissionError("权限不足")
return func(user, *args, **kwargs)
return wrapper
return decorator
@auth_required("admin")
def delete_user(admin, user_id):
# 执行删除操作
pass
3. API路由系统
Web框架通过装饰器建立URL与函数的映射关系:
routes = {}
def route(path):
def decorator(func):
routes[path] = func
return func
return decorator
@route("/home")
def home_page():
return "首页内容"
四、装饰器进阶技巧
带参数的装饰器
当需要动态配置装饰器行为时,需要三层嵌套结构:
def retry(max_attempts=3):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(max_attempts):
try:
return func(*args, **kwargs)
except:
pass
raise Exception("重试失败")
return wrapper
return decorator
@retry(max_attempts=5)
def fetch_data():
# 可能失败的远程调用
pass
装饰器堆叠顺序
多个装饰器会按照从下到上的顺序应用:
@decorator_a
@decorator_b
def target():
pass
# 实际执行顺序:decorator_b -> decorator_a -> target
保留原函数元信息
使用functools.wraps保持被装饰函数的身份信息:
import functools
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 装饰逻辑
return func(*args, **kwargs)
return wrapper
五、类装饰器的独特价值
当需要维护状态或实现复杂逻辑时,类装饰器更显优势:
class Counter:
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)
@Counter
def say_hello():
print("Hello")
say_hello() # 输出:调用次数:1 Hello
say_hello() # 输出:调用次数:2 Hello
类装饰器通过__call__方法实现实例的可调用特性,适合需要记录状态或进行复杂初始化的场景。
六、装饰器的设计哲学
- 单一职责原则:每个装饰器应聚焦解决单一问题
- 开闭原则:通过装饰而非修改原函数扩展功能
- 组合优于继承:通过装饰器链式组合实现复杂行为
- 透明性原则:装饰后的函数应保持与原函数相似的接口
七、常见误区解析
误区1:装饰器会改变原函数对象
实际装饰器创建的是新函数对象,原函数依然存在于内存中。
误区2:多层装饰器执行顺序混乱
记住装饰器应用顺序是自下而上的关键。
误区3:装饰器只能用于函数
实际上类装饰器可以装饰类,实现元编程效果。
八、实战案例:缓存装饰器
实现带超时控制的缓存装饰器:
import time
from functools import wraps
def cached(timeout=300):
cache = {}
def decorator(func):
@wraps(func)
def wrapper(*args):
key = (func, args)
if key in cache:
if time.time() - cache[key]["time"] < timeout:
return cache[key]["value"]
result = func(*args)
cache[key] = {"value": result, "time": time.time()}
return result
return wrapper
return decorator
@cached(timeout=10)
def expensive_calc(n):
time.sleep(2)
return n * 100
这个装饰器实现了:
- 基于函数和参数的缓存键生成
- 自动过期机制
- 线程不安全的简单实现(实际项目需加锁)
九、装饰器的性能考量
虽然装饰器带来代码复用优势,但需要注意:
- 避免在装饰器中执行耗时操作
- 谨慎使用全局状态(改用依赖注入)
- 对高频调用函数进行性能测试
- 考虑使用functools.lru_cache等内置缓存
十、总结:装饰器的核心价值
装饰器真正实现了"关注点分离"的编程理念,它像一把瑞士军刀,让我们能够:
- 将横切关注点(日志、缓存、权限)与业务逻辑解耦
- 通过组合而非继承扩展程序行为
- 构建可复用的功能模块
- 实现声明式编程风格
理解装饰器的工作原理,就像掌握了Python的"魔法咒语",能在不修改原函数的情况下,为其披上各种功能外衣。这种设计模式不仅提升了代码的可维护性,更让Python的灵活特性发挥得淋漓尽致。