🎯 装饰器练习:提升你的Python代码优雅度
装饰器是Python中一种强大的编程模式,可以帮助你编写更加简洁、优雅和可复用的代码。通过这些练习,你将深入理解装饰器的工作原理,并掌握如何在实际项目中应用装饰器。
🚀 练习目标
- 掌握基本装饰器的创建和使用
- 学习如何编写带参数的装饰器
- 理解多个装饰器的叠加使用
- 能够使用
functools.wraps保留原函数的元信息 - 掌握装饰器在常见场景中的应用
🔍 基础练习题
练习1:创建一个简单的日志装饰器
目标:创建一个名为simple_logger的装饰器,它会在函数执行前后打印一条消息。
要求:
- 装饰器应该在函数执行前打印"开始执行函数:[函数名]"
- 装饰器应该在函数执行后打印"函数 [函数名] 执行完成"
- 装饰器应该返回原函数的执行结果
示例:
python
@simple_logger
def add(a, b):
return a + b
# 调用函数时的输出:
# 开始执行函数:add
# 函数 add 执行完成
# 返回结果:5
result = add(2, 3)
print(f"返回结果:{result}")你的代码:
python
# 在这里实现你的装饰器
def simple_logger(func):
# 你的代码
# 测试代码
@simple_logger
def add(a, b):
return a + b
@simple_logger
def say_hello(name):
return f"Hello, {name}!"
result1 = add(5, 3)
print(f"返回结果:{result1}")
print("=" * 50)
result2 = say_hello("Python")
print(f"返回结果:{result2}")练习2:创建一个带参数的装饰器
目标:创建一个名为prefix_decorator的带参数装饰器,它会在函数返回的字符串前添加指定的前缀。
要求:
- 装饰器接收一个参数,表示要添加的前缀
- 装饰器应该在函数返回的字符串前添加这个前缀
- 装饰器应该保留原函数的元信息(使用
functools.wraps)
示例:
python
@prefix_decorator("[INFO]")
def get_message(name):
return f"Hello, {name}!"
# 调用函数时的输出:
# [INFO] Hello, Python!
message = get_message("Python")
print(message)你的代码:
python
import functools
# 在这里实现你的装饰器
def prefix_decorator(prefix):
# 你的代码
# 测试代码
@prefix_decorator("[ERROR]")
def get_error_message(error):
"""获取错误消息的函数"""
return f"发生错误: {error}"
@prefix_decorator("[SUCCESS]")
def get_success_message(message):
"""获取成功消息的函数"""
return message
# 测试函数
print(get_error_message("文件不存在"))
print(get_success_message("操作完成"))
# 验证元信息是否保留
print(f"函数名: {get_error_message.__name__}")
print(f"文档字符串: {get_error_message.__doc__}")练习3:创建一个缓存装饰器
目标:创建一个名为cache_decorator的装饰器,它会缓存函数的调用结果,对于相同的参数不再重复计算。
要求:
- 装饰器应该使用一个字典来缓存函数的调用结果
- 装饰器应该根据函数的参数来缓存结果
- 装饰器应该保留原函数的元信息
示例:
python
@cache_decorator
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 第一次调用时计算结果
print(f"第一次调用 fibonacci(10): {fibonacci(10)}")
# 第二次调用时从缓存获取结果
print(f"第二次调用 fibonacci(10): {fibonacci(10)}")你的代码:
python
import functools
# 在这里实现你的装饰器
def cache_decorator(func):
# 你的代码
# 测试代码 - 计算斐波那契数列
@cache_decorator
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 测试代码 - 计算阶乘
@cache_decorator
def factorial(n):
if n <= 1:
return 1
return n * factorial(n-1)
# 测试函数
print("计算 fibonacci(30)...")
import time
start = time.time()
result1 = fibonacci(30)
end = time.time()
print(f"结果: {result1}, 耗时: {end - start:.6f}秒")
print("再次计算 fibonacci(30)...")
start = time.time()
result2 = fibonacci(30)
end = time.time()
print(f"结果: {result2}, 耗时: {end - start:.6f}秒")
print("计算 factorial(20)...")
start = time.time()
result3 = factorial(20)
end = time.time()
print(f"结果: {result3}, 耗时: {end - start:.6f}秒")练习4:创建一个异常处理装饰器
目标:创建一个名为exception_handler的装饰器,它会捕获并处理函数执行过程中的异常。
要求:
- 装饰器应该捕获函数执行过程中的异常
- 装饰器应该打印异常信息
- 装饰器可以返回一个默认值或者重新抛出异常
- 装饰器应该保留原函数的元信息
示例:
python
@exception_handler
def divide(a, b):
return a / b
# 正常调用
print(f"divide(10, 2) = {divide(10, 2)}")
# 异常调用
print(f"divide(10, 0) = {divide(10, 0)}") # 应该打印异常信息并返回None你的代码:
python
import functools
# 在这里实现你的装饰器
def exception_handler(func):
# 你的代码
# 测试代码
@exception_handler
def parse_int(text):
return int(text)
@exception_handler
def open_file(filename):
with open(filename, 'r') as f:
return f.read()
# 测试函数
print(parse_int("123"))
print(parse_int("abc")) # 应该捕获ValueError
print(open_file("不存在的文件.txt")) # 应该捕获FileNotFoundError练习5:使用多个装饰器
目标:学会在一个函数上应用多个装饰器,并理解装饰器的执行顺序。
要求:
- 创建两个装饰器:
timer_decorator(计时)和log_decorator(日志) - 在一个函数上同时应用这两个装饰器
- 观察装饰器的执行顺序
提示:装饰器的应用顺序是从下到上(或从内到外)的
你的代码:
python
import time
import functools
# 创建计时装饰器
def timer_decorator(func):
@functools.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:.6f} 秒")
return result
return wrapper
# 创建日志装饰器
def log_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"开始执行函数: {func.__name__}")
print(f"参数: args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"函数执行完成,返回结果: {result}")
return result
return wrapper
# 同时应用两个装饰器
@timer_decorator
@log_decorator
def complex_operation(a, b, sleep_time=1):
"""一个复杂的操作,包含睡眠模拟耗时"""
time.sleep(sleep_time) # 模拟耗时操作
return a + b
# 测试函数
result = complex_operation(5, 3, sleep_time=0.5)
print(f"最终结果: {result}")
# 观察执行顺序,思考一下如果交换装饰器的顺序,结果会有什么不同?💪 挑战题
挑战题1:高级缓存装饰器
目标:创建一个高级缓存装饰器,支持设置缓存过期时间。
要求:
- 装饰器接收一个可选参数
expiry,表示缓存的过期时间(秒) - 当缓存项超过过期时间后,应该重新计算结果
- 装饰器应该支持清除缓存的功能
- 装饰器应该保留原函数的元信息
提示:
- 使用一个字典来存储缓存项和它们的创建时间
- 每次访问缓存时检查是否过期
- 可以添加一个清除缓存的方法
你的代码:
python
import functools
import time
# 在这里实现你的高级缓存装饰器
def advanced_cache_decorator(expiry=None):
# 你的代码
# 测试代码
@advanced_cache_decorator(expiry=2) # 缓存2秒后过期
def get_current_time():
"""获取当前时间的函数"""
print("计算当前时间...")
return time.time()
# 测试函数
print("第一次调用:")
print(get_current_time())
print("\n立即再次调用(应该使用缓存):")
print(get_current_time())
print("\n等待3秒后调用(缓存应该已过期):")
time.sleep(3)
print(get_current_time())
# 测试元信息保留
print(f"\n函数名: {get_current_time.__name__}")
print(f"文档字符串: {get_current_time.__doc__}")挑战题2:权限验证系统
目标:创建一个权限验证系统,使用装饰器来验证用户是否有权限执行特定操作。
要求:
- 创建一个名为
permission_required的装饰器,它接收一个权限列表 - 装饰器应该检查用户是否拥有列表中的任意一个权限
- 如果用户没有权限,装饰器应该抛出
PermissionError异常 - 装饰器应该保留原函数的元信息
- 创建一些测试函数和用户对象来验证装饰器的功能
提示:
- 假设用户对象有一个
permissions属性,是一个包含用户所有权限的列表 - 第一个参数应该是用户对象
你的代码:
python
import functools
# 在这里实现你的权限验证装饰器
def permission_required(permissions):
# 你的代码
# 创建一些测试函数
@permission_required(["admin", "editor"])
def create_post(user, title, content):
"""创建一篇博客文章"""
return f"用户 {user['name']} 创建了文章: {title}"
@permission_required(["admin"])
def delete_user(user, user_id):
"""删除用户"""
return f"用户 {user['name']} 删除了用户ID: {user_id}"
@permission_required(["view"])
def view_dashboard(user):
"""查看仪表盘"""
return f"用户 {user['name']} 查看了仪表盘"
# 创建一些测试用户
admin_user = {"name": "Admin", "permissions": ["admin", "editor", "view"]}
editor_user = {"name": "Editor", "permissions": ["editor", "view"]}
viewer_user = {"name": "Viewer", "permissions": ["view"]}
# 测试函数
print("=== 测试管理员用户 ===")
print(create_post(admin_user, "Python装饰器", "装饰器是一种强大的编程模式"))
print(delete_user(admin_user, 123))
print(view_dashboard(admin_user))
print("\n=== 测试编辑用户 ===")
print(create_post(editor_user, "Python基础", "Python是一种易学易用的编程语言"))
try:
delete_user(editor_user, 123) # 应该抛出PermissionError
except PermissionError as e:
print(f"权限错误: {e}")
print(view_dashboard(editor_user))
print("\n=== 测试查看用户 ===")
try:
create_post(viewer_user, "测试文章", "这是一篇测试文章") # 应该抛出PermissionError
except PermissionError as e:
print(f"权限错误: {e}")
try:
delete_user(viewer_user, 123) # 应该抛出PermissionError
except PermissionError as e:
print(f"权限错误: {e}")
print(view_dashboard(viewer_user))📝 部分参考答案
练习1参考答案
python
def simple_logger(func):
def wrapper(*args, **kwargs):
print(f"开始执行函数:{func.__name__}")
result = func(*args, **kwargs)
print(f"函数 {func.__name__} 执行完成")
return result
return wrapper练习2参考答案
python
import functools
def prefix_decorator(prefix):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return f"{prefix} {result}"
return wrapper
return decorator练习3参考答案
python
import functools
def cache_decorator(func):
cache = {}
@functools.wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper练习4参考答案
python
import functools
def exception_handler(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"函数 {func.__name__} 执行出错: {type(e).__name__}: {e}")
return None
return wrapper练习5提示
当我们使用@timer_decorator和@log_decorator装饰器时,相当于执行:
python
complex_operation = timer_decorator(log_decorator(complex_operation))所以执行顺序是:
timer_decorator的wrapper开始执行log_decorator的wrapper开始执行- 原函数
complex_operation执行 log_decorator的wrapper结束执行timer_decorator的wrapper结束执行
如果交换装饰器的顺序,执行顺序也会相应改变。
🎉 完成挑战!
恭喜你完成了所有的装饰器练习!通过这些练习,你已经掌握了装饰器的基本概念、工作原理和常见应用场景。装饰器是Python中一种非常强大的编程模式,可以帮助你编写更加优雅、可复用的代码。继续加油,将装饰器应用到你的实际项目中吧! 💪




