Skip to content

🎯 装饰器练习:提升你的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))

所以执行顺序是:

  1. timer_decorator的wrapper开始执行
  2. log_decorator的wrapper开始执行
  3. 原函数complex_operation执行
  4. log_decorator的wrapper结束执行
  5. timer_decorator的wrapper结束执行

如果交换装饰器的顺序,执行顺序也会相应改变。

🎉 完成挑战!

恭喜你完成了所有的装饰器练习!通过这些练习,你已经掌握了装饰器的基本概念、工作原理和常见应用场景。装饰器是Python中一种非常强大的编程模式,可以帮助你编写更加优雅、可复用的代码。继续加油,将装饰器应用到你的实际项目中吧! 💪

© 2025 技术博客. All rights reserved by 老周有AI