# Python装饰器详解:让你的代码更优雅
装饰器是Python中一种强大的语法糖,可以在不修改原函数的情况下,增强函数的功能。本文详细介绍装饰器的原理和使用方法,让你的Python代码更加优雅和高效。
# 一、装饰器基础
# 什么是装饰器
# 装饰器本质是一个函数,接收函数作为参数,返回增强后的函数
def my_decorator(func):
"""装饰器函数"""
def wrapper(*args, **kwargs):
print("函数执行前...")
result = func(*args, **kwargs)
print("函数执行后...")
return result
return wrapper
# 使用装饰器
@my_decorator
def say_hello(name):
print(f"你好,{name}!")
# 等价于
# say_hello = my_decorator(say_hello)
# 调用
say_hello("程序员晚枫")
输出:
函数执行前...
你好,程序员晚枫!
函数执行后...
# 带参数的装饰器
def log_decorator(prefix="LOG"):
"""带参数的装饰器工厂"""
def decorator(func):
def wrapper(*args, **kwargs):
print(f"[{prefix}] 开始执行: {func.__name__}")
result = func(*args, **kwargs)
print(f"[{prefix}] 执行完成: {func.__name__}")
return result
return wrapper
return decorator
@log_decorator(prefix="INFO")
def add(a, b):
return a + b
@log_decorator(prefix="DEBUG")
def multiply(a, b):
return a * b
# 使用
print(add(10, 20))
print(multiply(10, 20))
# 二、装饰器高级用法
# 保留原函数元信息
from functools import wraps
def my_decorator(func):
@wraps(func) # 保留原函数的元信息
def wrapper(*args, **kwargs):
"""这是wrapper的文档"""
print("执行前")
result = func(*args, **kwargs)
print("执行后")
return result
return wrapper
@my_decorator
def original_function():
"""这是原始函数的文档"""
print("执行中")
# 测试元信息
print(f"函数名: {original_function.__name__}")
print(f"文档: {original_function.__doc__}")
# 类装饰器
class CallCounter:
"""统计函数调用次数的装饰器"""
def __init__(self, func):
self.func = func
self.count = 0
wraps(func)(self) # 保留元信息
def __call__(self, *args, **kwargs):
self.count += 1
print(f"函数 {self.func.__name__} 被调用了 {self.count} 次")
return self.func(*args, **kwargs)
@CallCounter
def fetch_data():
return "数据"
# 使用
fetch_data() # 调用1次
fetch_data() # 调用2次
fetch_data() # 调用3次
print(f"总计调用: {fetch_data.count} 次")
# 多装饰器叠加
def decorator1(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("decorator1 执行")
return func(*args, **kwargs)
return wrapper
def decorator2(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("decorator2 执行")
return func(*args, **kwargs)
return wrapper
# 装饰器从下到上执行
@decorator1
@decorator2
def test():
print("test 执行")
# 等价于: decorator1(decorator2(test()))
test()
# 输出顺序:
# decorator1 执行
# decorator2 执行
# test 执行
# 三、常用装饰器示例
# 计时装饰器
import time
from functools import wraps
def timer(func):
"""函数执行计时装饰器"""
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"函数 {func.__name__} 执行耗时: {end - start:.4f} 秒")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
return "完成"
@timer
def calculate_sum(n):
return sum(range(n))
# 使用
slow_function()
calculate_sum(1000000)
# 日志装饰器
import logging
from functools import wraps
def log_calls(logger=None):
"""记录函数调用日志"""
if logger is None:
logger = logging.getLogger(__name__)
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
args_repr = [repr(a) for a in args]
kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
signature = ", ".join(args_repr + kwargs_repr)
logger.info(f"调用 {func.__name__}({signature})")
try:
result = func(*args, **kwargs)
logger.info(f"{func.__name__} 返回: {result!r}")
return result
except Exception as e:
logger.exception(f"{func.__name__} 发生异常: {e}")
raise
return wrapper
return decorator
# 使用
@log_calls()
def divide(a, b):
return a / b
divide(10, 2)
divide(10, 0)
# 缓存装饰器
from functools import lru_cache
import time
# 使用lru_cache实现缓存
@lru_cache(maxsize=128)
def expensive_calculation(n):
"""耗时的计算"""
time.sleep(1) # 模拟耗时操作
return n * n
print("第一次调用:")
start = time.time()
print(f"结果: {expensive_calculation(100)}")
print(f"耗时: {time.time() - start:.4f}s")
print("\n第二次调用(缓存):")
start = time.time()
print(f"结果: {expensive_calculation(100)}")
print(f"耗时: {time.time() - start:.4f}s")
# 重试装饰器
import time
from functools import wraps
def retry(max_attempts=3, delay=1, backoff=2, exceptions=(Exception,)):
"""重试装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
attempts = 0
current_delay = delay
while attempts < max_attempts:
try:
return func(*args, **kwargs)
except exceptions as e:
attempts += 1
if attempts >= max_attempts:
print(f"达到最大重试次数 {max_attempts},放弃")
raise
print(f"第 {attempts} 次尝试失败,{current_delay}秒后重试... 错误: {e}")
time.sleep(current_delay)
current_delay *= backoff # 指数退避
return None
return wrapper
return decorator
@retry(max_attempts=3, delay=1, exceptions=(ConnectionError,))
def fetch_data():
import random
if random.random() < 0.7:
raise ConnectionError("网络不稳定")
return "数据获取成功"
# 测试
try:
result = fetch_data()
print(f"最终结果: {result}")
except Exception as e:
print(f"最终失败: {e}")
# 四、类中的装饰器
# 方法装饰器
class DataProcessor:
def __init__(self):
self.data = []
def timer(method):
"""方法计时装饰器"""
@wraps(method)
def wrapper(self, *args, **kwargs):
start = time.time()
result = method(self, *args, **kwargs)
print(f"{method.__name__} 执行耗时: {time.time() - start:.4f}s")
return result
return wrapper
@timer
def load_data(self, path):
time.sleep(0.5)
self.data = [1, 2, 3, 4, 5]
return len(self.data)
@timer
def process_data(self):
time.sleep(0.3)
return [x * 2 for x in self.data]
# 使用
processor = DataProcessor()
processor.load_data('data.csv')
processor.process_data()
# 属性装饰器
class User:
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
"""姓名属性(只读)"""
return self._name
@property
def age(self):
"""年龄属性,带验证"""
return self._age
@age.setter
def age(self, value):
if value < 0:
raise ValueError("年龄不能为负数")
self._age = value
# 使用
user = User("张三", 25)
print(f"姓名: {user.name}")
print(f"年龄: {user.age}")
user.age = 30
print(f"修改后的年龄: {user.age}")
# 五、装饰器应用场景
| 装饰器类型 | 应用场景 | 示例 |
|---|---|---|
| 计时器 | 性能分析 | @timer |
| 日志 | 记录调用 | @log_calls |
| 缓存 | 加速重复计算 | @lru_cache |
| 重试 | 网络请求 | @retry |
| 权限 | 访问控制 | @requires_auth |
| 验证 | 参数检查 | @validate_params |
| 节流 | 限制调用频率 | @throttle |
# 相关资源
- python-office 官方文档 - Python自动化办公库
- AI + 自动化办公课程 - 35讲AI办公自动化实战
- Python 实用技巧 - 更多Python技巧
- Python 异常处理 - 异常处理进阶
- 📺 视频课程:Python入门15讲
- 👥 技术交流:加入讨论
# 总结
装饰器是Python最强大的特性之一。掌握装饰器的原理和使用方法,可以让代码更加模块化、可复用,提升开发效率。多多练习,将装饰器应用到实际项目中去吧!