你有没有遇到过这种情况:
项目里到处都是这样的代码:
python
def user_login():
start_time = time.time()
print(f"[{datetime.now()}] 开始执行用户登录")
try:
# 业务逻辑
result = authenticate_user()
print(f"[{datetime.now()}] 用户登录成功")
return result
except Exception as e:
print(f"[{datetime.now()}] 用户登录失败: {e}")
raise
finally:
end_time = time.time()
print(f"[{datetime.now()}] 登录耗时: {end_time - start_time:.2f}秒")
def user_register():
start_time = time.time()
print(f"[{datetime.now()}] 开始执行用户注册")
try:
# 业务逻辑
result = create_user()
print(f"[{datetime.now()}] 用户注册成功")
return result
except Exception as e:
print(f"[{datetime.now()}] 用户注册失败: {e}")
raise
finally:
end_time = time.time()
print(f"[{datetime.now()}] 注册耗时: {end_time - start_time:.2f}秒")
每个函数都要重复写日志、异常处理、性能统计的代码...
然后你百度了一圈,Stack Overflow翻了三页,最后发现有个叫"装饰器"的东西。
今天咱们就来彻底搞懂这个让代码瞬间变优雅的神器。
装饰器到底是啥玩意儿?
一句话解释:装饰器就是一个"包装工",它把你的函数重新包装一下,加点新功能。
想象一下你去奶茶店:
ini
原始函数 = 基础奶茶(珍珠奶茶)
装饰器 = 加料服务(加珍珠、加椰果、加布丁)
@decorator = 直接说"我要加珍珠"
你没加装饰器之前:
python
# 普通珍珠奶茶
def get_milk_tea():
return "珍珠奶茶"
加了装饰器之后:
python
@add_pearls # 相当于:get_milk_tea = add_pearls(get_milk_tea)
def get_milk_tea():
return "珍珠奶茶"
# 现在这杯奶茶自动加了珍珠,还送了你吸管
本质上,@decorator 只是语法糖,真正的执行是:
python
# 这两种写法完全等价:
@decorator
def func():
pass
# 等价于:
def func():
pass
func = decorator(func)
先看看最简单的装饰器
python
import time
from functools import wraps
def timer_decorator(func):
@wraps(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:.2f}秒")
return result
return wrapper
@timer_decorator
def slow_function():
time.sleep(1)
return "我执行完了"
result = slow_function()
# 输出: slow_function 执行耗时: 1.00秒
看到了吗?原本需要每个函数都写的计时代码,现在只需要一个@timer_decorator就搞定了!
但是!别急着用,先搞懂这几个坑
坑1:函数信息丢失的问题
❌ 错误写法:
python
def timer_decorator(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:.2f}秒")
return result
return wrapper
@timer_decorator
def my_awesome_function():
"""这个函数很厉害,能拯救世界"""
pass
print(my_awesome_function.__name__) # 输出: wrapper (不是my_awesome_function!)
print(my_awesome_function.__doc__) # 输出: None (文档说明丢了!)
✅ 正确写法:
python
from functools import wraps
def timer_decorator(func):
@wraps(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:.2f}秒")
return result
return wrapper
@timer_decorator
def my_awesome_function():
"""这个函数很厉害,能拯救世界"""
pass
print(my_awesome_function.__name__) # 输出: my_awesome_function ✓
print(my_awesome_function.__doc__) # 输出: 这个函数很厉害,能拯救世界 ✓
@wraps(func) 就是函数信息的"复印机",把原函数的名字、文档、参数信息全部复制到装饰器函数上。
坑2:带参数的装饰器
有时候你想给装饰器传参数,比如:
python
@repeat(3) # 执行3次
def test_function():
print("测试中...")
这时候就需要装饰器工厂:
python
def repeat(times):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
results = []
for i in range(times):
print(f"第 {i+1} 次执行:")
result = func(*args, **kwargs)
results.append(result)
return results
return wrapper
return decorator
@repeat(3)
def test_function():
print("测试中...")
return "成功"
# 等价于:test_function = repeat(3)(test_function)
results = test_function()
记住这个公式:带参数的装饰器 = 三层嵌套函数
坑3:装饰器的执行顺序
python
@decorator1
@decorator2
def my_function():
pass
# 执行顺序相当于:my_function = decorator1(decorator2(my_function))
# 也就是:先执行decorator2,再执行decorator1
实战:解决真实项目中的重复代码
场景1:API接口的日志记录
❌ 重复代码地狱:
python
def get_user_info(user_id):
print(f"[{datetime.now()}] API调用: get_user_info, 参数: {user_id}")
try:
result = database.query_user(user_id)
print(f"[{datetime.now()}] API调用成功: 返回用户信息")
return {"status": "success", "data": result}
except DatabaseError as e:
print(f"[{datetime.now()}] 数据库错误: {e}")
return {"status": "error", "message": "数据库查询失败"}
except Exception as e:
print(f"[{datetime.now()}] 未知错误: {e}")
return {"status": "error", "message": "服务器内部错误"}
def create_user(user_data):
print(f"[{datetime.now()}] API调用: create_user, 参数: {user_data}")
try:
result = database.insert_user(user_data)
print(f"[{datetime.now()}] API调用成功: 用户创建成功")
return {"status": "success", "data": result}
except DatabaseError as e:
print(f"[{datetime.now()}] 数据库错误: {e}")
return {"status": "error", "message": "数据库插入失败"}
except Exception as e:
print(f"[{datetime.now()}] 未知错误: {e}")
return {"status": "error", "message": "服务器内部错误"}
✅ 优雅的装饰器方案:
python
from functools import wraps
import json
from datetime import datetime
def api_logger(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 记录请求
print(f"[{datetime.now()}] API调用: {func.__name__}, 参数: {args}, {kwargs}")
try:
result = func(*args, **kwargs)
print(f"[{datetime.now()}] API调用成功: {func.__name__}")
return {"status": "success", "data": result}
except DatabaseError as e:
print(f"[{datetime.now()}] 数据库错误: {e}")
return {"status": "error", "message": "数据库操作失败"}
except Exception as e:
print(f"[{datetime.now()}] 未知错误: {e}")
return {"status": "error", "message": "服务器内部错误"}
return wrapper
@api_logger
def get_user_info(user_id):
return database.query_user(user_id)
@api_logger
def create_user(user_data):
return database.insert_user(user_data)
# 看看多简洁!业务逻辑和日志处理完全分离
场景2:权限验证
python
def admin_required(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 假设从session或token中获取用户信息
current_user = get_current_user()
if not current_user:
raise PermissionError("请先登录")
if not current_user.is_admin:
raise PermissionError("需要管理员权限")
return func(*args, **kwargs)
return wrapper
@admin_required
def delete_user(user_id):
# 只有管理员能删除用户
database.delete_user(user_id)
@admin_required
def view_system_logs():
# 只有管理员能查看系统日志
return database.get_system_logs()
场景3:缓存装饰器
python
import time
from functools import wraps
def cache(expire_time=60):
"""缓存装饰器,expire_time单位是秒"""
def decorator(func):
func._cache = {} # 把缓存存储在函数的属性里
@wraps(func)
def wrapper(*args, **kwargs):
# 生成缓存key(把参数转成字符串)
cache_key = str(args) + str(sorted(kwargs.items()))
current_time = time.time()
# 检查缓存是否存在且未过期
if cache_key in func._cache:
cached_result, cached_time = func._cache[cache_key]
if current_time - cached_time < expire_time:
print(f"使用缓存: {func.__name__}({cache_key})")
return cached_result
# 执行原函数
result = func(*args, **kwargs)
# 存入缓存
func._cache[cache_key] = (result, current_time)
print(f"缓存结果: {func.__name__}({cache_key})")
return result
return wrapper
return decorator
@cache(expire_time=30) # 缓存30秒
def expensive_calculation(x, y):
print(f"执行耗时计算: {x} + {y}")
time.sleep(2) # 模拟耗时操作
return x + y
# 第一次调用会执行计算
result1 = expensive_calculation(1, 2) # 2秒后才返回结果
# 第二次调用直接从缓存返回,瞬间完成
result2 = expensive_calculation(1, 2) # 瞬间返回结果
高级玩法:类装饰器
有时候装饰器需要保存状态,用类更方便:
python
class CountCalls:
def __init__(self, func):
self.func = func
self.call_count = 0
wraps(func)(self) # 让类实例表现得像原函数
def __call__(self, *args, **kwargs):
self.call_count += 1
print(f"{self.func.__name__} 被调用了 {self.call_count} 次")
return self.func(*args, **kwargs)
@CountCalls
def hello():
print("Hello!")
hello() # 输出: hello 被调用了 1 次
hello() # 输出: hello 被调用了 2 次
hello() # 输出: hello 被调用了 3 次
print(f"总共调用了 {hello.call_count} 次")
总结:什么时候用装饰器?
适合用装饰器的场景:
- ✅ 重复的功能代码(日志、计时、缓存、权限验证)
- ✅ 想在不修改原函数的情况下增加功能
- ✅ 需要横切多个函数的关注点(AOP思想)
- ✅ 想让代码更优雅、更可维护
不适合用装饰器的场景:
- ❌ 只在一个函数里用的逻辑
- ❌ 和原函数逻辑耦合太紧的功能
- ❌ 调试时需要频繁查看函数内部状态的情况
记住一句话:装饰器是代码的"化妆品",不是"手术刀"。它用来美化代码,不是用来修改核心逻辑的。
下次再看到满屏的重复代码,你就知道该怎么办了。
别让你的代码看起来像个垃圾堆,用装饰器让它优雅起来!