Python装饰器:魔法棒般的神奇工具
一、介绍:给代码穿上"超级战衣"
想象一下,你有一件普通T恤(普通函数),但当你给它套上钢铁侠战甲(装饰器)时,它就获得了飞天遁地的超能力------这就是装饰器的魔法!装饰器本质上是个"函数包装器",能在不修改原函数代码的前提下,为函数增添新功能。
为什么需要它?
- 避免重复代码(DRY原则)
- 分离核心逻辑与辅助功能(如日志、缓存)
- 让代码像乐高积木一样灵活组合
python
# 举个栗子🌰:普通函数
def say_hello():
print("Hello!")
# 加了"超级战衣"的函数
@super_armor
def say_hello():
print("Hello!")
二、用法大全:装饰器的七十二变
1. 基础款:函数装饰器
python
def debug_decorator(func):
def wrapper(*args, **kwargs):
print(f"🚀 呼叫函数: {func.__name__}()")
result = func(*args, **kwargs)
print(f"✅ 函数执行完毕")
return result
return wrapper
@debug_decorator
def add(a, b):
return a + b
print(add(2, 3))
# 输出:
# 🚀 呼叫函数: add()
# ✅ 函数执行完毕
# 5
2. 进阶款:带参数的装饰器
python
def repeat(times):
# 这是个装饰器工厂
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
# 输出:
# Hello, Alice!
# Hello, Alice!
# Hello, Alice!
3. 豪华款:类装饰器
python
class Timer:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
import time
start = time.time()
result = self.func(*args, **kwargs)
end = time.time()
print(f"⏱️ 函数 {self.func.__name__} 耗时: {end-start:.4f}秒")
return result
@Timer
def heavy_calculation():
return sum(i*i for i in range(10**6))
heavy_calculation() # ⏱️ 函数 heavy_calculation 耗时: 0.1253秒
4. 隐藏款:装饰类方法
python
def inject_user(func):
def wrapper(self, *args, **kwargs):
if not hasattr(self, 'user'):
self.user = "管理员" # 自动注入属性
return func(self, *args, **kwargs)
return wrapper
class Dashboard:
@inject_user
def show(self):
print(f"欢迎, {self.user}!")
dash = Dashboard()
dash.show() # 欢迎, 管理员!
三、实战案例:装饰器的超级英雄联盟
1. 性能检测器
python
def benchmark(func):
import time
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"⚡ {func.__name__} 执行时间: {elapsed:.6f}秒")
return result
return wrapper
2. 自动缓存器
python
def cache(func):
stored = {}
def wrapper(*args):
if args in stored:
print(f"♻️ 从缓存读取: {args}")
return stored[args]
result = func(*args)
stored[args] = result
return result
return wrapper
@cache
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10)) # 只计算一次,后续从缓存读取
3. 权限守卫
python
def require_role(role):
def decorator(func):
def wrapper(user, *args, **kwargs):
if user.get('role') != role:
raise PermissionError("🚫 权限不足!")
return func(user, *args, **kwargs)
return wrapper
return decorator
@require_role("admin")
def delete_database(user):
print("💥 数据库已删除!")
user = {"name": "Bob", "role": "user"}
delete_database(user) # 抛出 PermissionError
四、原理解析:装饰器如何施展魔法
装饰器本质是语法糖,以下两种写法等价:
python
@decorator
def func(): pass
# 等同于
func = decorator(func)
闭包是关键:
- 外层函数接收目标函数
- 内层函数执行额外操作并调用原函数
- 返回内层函数替代原函数
graph LR
A[原函数] --> B[装饰器]
B --> C[新函数]
C --> D[执行额外操作]
D --> E[调用原函数]
五、横向对比:装饰器 vs 其他方案
方案 | 优点 | 缺点 |
---|---|---|
装饰器 | 非侵入式,可复用 | 理解成本稍高 |
直接修改函数 | 简单直接 | 破坏原有逻辑,难复用 |
继承 | 符合OOP原则 | 结构臃肿,不适用所有场景 |
组合模式 | 灵活性强 | 实现复杂,需要额外类 |
💡 真相时刻 :装饰器本质是高阶函数+闭包+语法糖的组合拳!
六、避坑指南:装饰器的七宗罪
陷阱1:丢失函数元信息
python
@debug_decorator
def say_hi(): pass
print(say_hi.__name__) # 输出 'wrapper' 😱
修复方案 :使用functools.wraps
python
from functools import wraps
def debug_decorator(func):
@wraps(func) # 保留原函数信息
def wrapper(*args, **kwargs):
...
return wrapper
陷阱2:多层装饰器顺序混乱
python
@decoratorA
@decoratorB
def func(): pass
# 实际执行顺序: decoratorA(decoratorB(func))
陷阱3:装饰类方法时丢失self
python
def bad_decorator(func):
def wrapper(): # 漏了*args!
return func()
return wrapper
class MyClass:
@bad_decorator
def method(self):
print(self)
obj = MyClass()
obj.method() # TypeError: method() missing 1 required positional argument: 'self'
七、最佳实践:写出工业级装饰器
-
总是使用
functools.wraps
pythonfrom functools import wraps def decorator(func): @wraps(func) def wrapper(*args, **kwargs): ...
-
支持关键字参数
pythondef universal_decorator(func): def wrapper(*args, **kwargs): # 同时接受位置和关键字参数 ...
-
为装饰器编写单元测试
pythondef test_decorator(): @decorator def test_func(): return 42 assert test_func() == 42 # 检查日志等副作用
-
避免状态副作用
python# 错误示范:共享状态! def counter_decorator(func): count = 0 def wrapper(*args, **kwargs): nonlocal count count += 1 print(f"调用次数: {count}") return func(*args, **kwargs) return wrapper # 正确做法:使用函数属性 def counter_decorator(func): def wrapper(*args, **kwargs): wrapper.count += 1 print(f"调用次数: {wrapper.count}") return func(*args, **kwargs) wrapper.count = 0 return wrapper
八、面试考点:装饰器灵魂三问
Q1:解释装饰器执行过程
python
@decoA
@decoB
def my_func(): pass
✅ 参考答案:
- 最内层装饰器先执行:
temp = decoB(my_func)
- 外层装饰器再执行:
final_func = decoA(temp)
- 最终调用
final_func()
时,执行顺序:
decoA逻辑前 -> decoB逻辑前 -> my_func() -> decoB逻辑后 -> decoA逻辑后
Q2:如何实现带可选参数的装饰器? ✅ 参考答案:使用三层嵌套
python
def smart_decorator(arg=None):
def middle_layer(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 根据arg判断行为
return func(*args, **kwargs)
return wrapper
if callable(arg): # 直接使用装饰器
return middle_layer(arg)
return middle_layer # 带参数调用
Q3:为什么装饰器会改变函数签名?如何保留? ✅ 参考答案:
- 原因:装饰器返回的新函数覆盖了原函数
- 解决方案:
- 使用
functools.wraps
- 使用
inspect
模块动态获取签名 - 第三方库
decorator
的@decorator
装饰器
- 使用
九、总结:装饰器的智慧之光
✨ 核心价值:
- 开放封闭原则:对扩展开放,对修改封闭
- 关注点分离:核心逻辑 vs 辅助功能
- 代码即乐高:通过组合创造无限可能
🚀 使用场景推荐:
- 日志记录/性能监控
- 权限校验/输入验证
- 缓存/记忆化
- 错误重试机制
- 路由注册(如Flask框架)
终极哲学:装饰器不是银弹,但当你需要"不动原函数而增强能力"时,它就是你的代码炼金术!
python
# 用装饰器开启新征程
@magic_decorator
def start_coding():
print("🚀 向着Python宇宙进发!")
start_coding()