Python 零基础入门系列(十):闭包和装饰器
🐍 Python 零基础入门系列(十):闭包和装饰器
📢 系列说明:
欢迎来到《Python 零基础入门系列》第十篇!前九篇我们掌握了基础语法、数据类型、流程控制、函数基础和进阶知识。📅 更新时间:2026 年 3 月 26 日
🎯 本篇你将学到:闭包原理、装饰器基础、装饰器进阶、内置装饰器,以及 3 个装饰器实战练习。
⏱️ 预计阅读时间:60-70 分钟 | 💻 实践时间:50-60 分钟
📌 前置知识:已完成前九篇(特别是函数进阶知识)
✍️ 作者:书到用时方恨少!
注:本章节课程难度较高,部分内容超出基础知识范畴,大家可做一个简单了解,不要求掌握
📋 完整目录
┌─────────────────────────────────────────────────────────────┐
│ 本篇博客内容导航 │
├─────────────────────────────────────────────────────────────┤
│ 第一部分:闭包深入 │
│ ├── 1. 闭包的概念和原理 │
│ ├── 2. 闭包的创建条件 │
│ ├── 3. 闭包的实际应用 │
│ └── 4. 闭包的陷阱和解决方案 │
├─────────────────────────────────────────────────────────────┤
│ 第二部分:装饰器基础 │
│ ├── 5. 装饰器的概念和语法 │
│ ├── 6. 无参数装饰器 │
│ ├── 7. 有参数装饰器 │
│ └── 8. 多个装饰器叠加 │
├─────────────────────────────────────────────────────────────┤
│ 第三部分:装饰器进阶 │
│ ├── 9. 带参数的装饰器 │
│ ├── 10. 类装饰器 │
│ ├── 11. 装饰器中的 functools.wraps │
│ └── 12. 装饰器最佳实践 │
├─────────────────────────────────────────────────────────────┤
│ 第四部分:内置装饰器 │
│ ├── 13. @staticmethod 静态方法 │
│ ├── 14. @classmethod 类方法 │
│ └── 15. @property 属性装饰器 │
├─────────────────────────────────────────────────────────────┤
│ 第五部分:综合实战练习 │
│ ├── 16. 练习一:日志装饰器 │
│ ├── 17. 练习二:计时装饰器 │
│ └── 18. 练习三:缓存装饰器 │
└─────────────────────────────────────────────────────────────┘
第一部分:闭包深入
1. 闭包的概念和原理
📖 什么是闭包
闭包(Closure) 是指在一个外函数中定义了一个内函数,内函数引用了外函数的局部变量,并且外函数返回了这个内函数。
🔍 闭包的三要素
┌─────────────────────────────────────────────────────────────┐
│ 闭包的三要素 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1️⃣ 必须有一个内嵌函数(函数内部定义函数) │
│ │
│ 2️⃣ 内嵌函数必须引用外函数的局部变量 │
│ │
│ 3️⃣ 外函数必须返回内嵌函数 │
│ │
└─────────────────────────────────────────────────────────────┘
🔍 基本示例
# 闭包示例
def outer_function(x):
"""外函数"""
def inner_function(y):
"""内函数 - 引用了外函数的 x"""
return x + y
return inner_function # 返回内函数
# 使用闭包
add_5 = outer_function(5) # x = 5
print(add_5(3)) # 输出:8 (5 + 3)
print(add_5(10)) # 输出:15 (5 + 10)
# 再创建一个闭包
add_10 = outer_function(10) # x = 10
print(add_10(3)) # 输出:13 (10 + 3)
🔍 闭包原理图解
┌─────────────────────────────────────────────────────────────┐
│ 闭包执行过程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 步骤 1: 调用 outer_function(5) │
│ ┌─────────────────┐ │
│ │ outer_function │ x = 5 │
│ │ │ │
│ │ ┌───────────┐ │ │
│ │ │inner_func │ │ 引用 x │
│ │ └───────────┘ │ │
│ │ │ │
│ │ return inner ─┼──────────────┐ │
│ └─────────────────┘ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ add_5 闭包 │ │
│ │ 保存了 x = 5 │ │
│ └─────────────────┘ │
│ │
│ 步骤 2: 调用 add_5(3) │
│ ┌─────────────────┐ │
│ │ add_5 闭包 │ x = 5 (从闭包中获取) │
│ │ │ y = 3 (新参数) │
│ │ return 5 + 3 │ 结果:8 │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
2. 闭包的创建条件
🔍 验证闭包
def outer(x):
def inner(y):
return x + y
return inner
# 创建闭包
closure = outer(10)
# 查看闭包信息
print(f"闭包函数:{closure}")
print(f"闭包变量:{closure.__closure__}")
print(f"自由变量:{outer.__code__.co_freevars}")
# 获取闭包中的值
if closure.__closure__:
for i, cell in enumerate(closure.__closure__):
print(f"闭包变量 {i}: {cell.cell_contents}")
🔍 非闭包示例对比
# ❌ 不是闭包:内函数没有引用外函数变量
def outer1(x):
def inner(y):
return y * 2 # 没有使用 x
return inner
# ❌ 不是闭包:外函数没有返回内函数
def outer2(x):
def inner(y):
return x + y
inner(5) # 直接调用,没有返回
return None
# ✅ 是闭包
def outer3(x):
def inner(y):
return x + y # 引用了 x
return inner # 返回内函数
3. 闭包的实际应用
🔍 应用 1:计数器
def create_counter():
"""创建计数器"""
count = 0 # 外部变量
def counter():
nonlocal count # 声明使用外部变量
count += 1
return count
return counter
# 使用
counter1 = create_counter()
print(counter1()) # 1
print(counter1()) # 2
print(counter1()) # 3
counter2 = create_counter() # 新的计数器
print(counter2()) # 1 (独立的计数器)
print(counter1()) # 4 (counter1 继续计数)
🔍 应用 2:函数工厂
def create_multiplier(factor):
"""创建倍数乘法器"""
def multiplier(x):
return x * factor
return multiplier
# 创建不同的乘法器
double = create_multiplier(2)
triple = create_multiplier(3)
ten_times = create_multiplier(10)
print(f"double(5) = {double(5)}") # 10
print(f"triple(5) = {triple(5)}") # 15
print(f"ten_times(5) = {ten_times(5)}") # 50
🔍 应用 3:数据隐藏
def create_bank_account(initial_balance):
"""创建银行账户(隐藏余额)"""
balance = initial_balance
def deposit(amount):
nonlocal balance
balance += amount
return f"存入 {amount}, 当前余额:{balance}"
def withdraw(amount):
nonlocal balance
if amount > balance:
return "余额不足!"
balance -= amount
return f"取出 {amount}, 当前余额:{balance}"
def get_balance():
return balance
# 返回操作接口
return {
'deposit': deposit,
'withdraw': withdraw,
'get_balance': get_balance
}
# 使用
account = create_bank_account(1000)
print(account['deposit'](500)) # 存入 500, 当前余额:1500
print(account['withdraw'](200)) # 取出 200, 当前余额:1300
print(f"余额:{account['get_balance']}") # 1300
# 无法直接访问 balance,实现了数据隐藏
# print(account['balance']) # KeyError
4. 闭包的陷阱和解决方案
⚠️ 陷阱 1:循环变量捕获
# ❌ 错误示例
functions = []
for i in range(5):
functions.append(lambda: i)
# 调用结果
print([f() for f in functions]) # [4, 4, 4, 4, 4] 全是 4!
# 原因:所有 lambda 都引用了同一个 i,循环结束后 i = 4
✅ 解决方案
# 方案 1:使用默认参数
functions = []
for i in range(5):
functions.append(lambda x=i: x)
print([f() for f in functions]) # [0, 1, 2, 3, 4] ✅
# 方案 2:使用闭包
functions = []
for i in range(5):
def make_func(x):
return lambda: x
functions.append(make_func(i))
print([f() for f in functions]) # [0, 1, 2, 3, 4] ✅
⚠️ 陷阱 2:可变对象的引用
# ❌ 错误示例
def create_functions():
funcs = []
for i in range(3):
funcs.append(lambda: i)
return funcs
# ✅ 正确示例
def create_functions_fixed():
funcs = []
for i in range(3):
funcs.append(lambda x=i: x)
return funcs
⚠️ 陷阱 3:nonlocal 的使用
# ❌ 错误:忘记使用 nonlocal
def counter_broken():
count = 0
def increment():
count += 1 # UnboundLocalError!
return count
return increment
# ✅ 正确:使用 nonlocal
def counter_fixed():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
┌─────────────────────────────────────────────────────────────┐
│ 闭包使用建议 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ✅ 最佳实践 │
│ • 使用 default 参数捕获循环变量 │
│ • 修改外部变量时使用 nonlocal │
│ • 避免在闭包中引用可变对象 │
│ • 及时清理不需要的闭包引用 │
│ │
│ ⚠️ 注意事项 │
│ • 闭包会占用内存(变量不会释放) │
│ • 循环中创建闭包要特别注意变量捕获 │
│ • 闭包中的变量是引用,不是副本 │
│ │
└─────────────────────────────────────────────────────────────┘
第二部分:装饰器基础
5. 装饰器的概念和语法
📖 什么是装饰器
装饰器(Decorator) 是一种特殊的函数,用于在不修改原函数代码的情况下,为函数添加额外功能。
🔍 装饰器本质
┌─────────────────────────────────────────────────────────────┐
│ 装饰器本质 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 装饰器 = 高阶函数 + 闭包 │
│ │
│ • 接受一个函数作为参数 │
│ • 返回一个新的函数 │
│ • 新函数包含原函数功能 + 额外功能 │
│ │
└─────────────────────────────────────────────────────────────┘
🔍 装饰器语法
# 装饰器语法糖
@decorator
def my_function():
pass
# 等价于
def my_function():
pass
my_function = decorator(my_function)
🔍 简单装饰器示例
def simple_decorator(func):
"""简单装饰器"""
def wrapper():
print("函数执行前")
func()
print("函数执行后")
return wrapper
# 使用装饰器
@simple_decorator
def say_hello():
print("Hello!")
# 调用
say_hello()
# 输出:
# 函数执行前
# Hello!
# 函数执行后
6. 无参数装饰器
🔍 装饰无参数函数
def logger_decorator(func):
"""日志装饰器"""
def wrapper():
print(f"[LOG] 开始执行 {func.__name__}")
result = func()
print(f"[LOG] 执行完成 {func.__name__}")
return result
return wrapper
@logger_decorator
def greet():
print("你好!")
return "greet 完成"
# 调用
result = greet()
print(result)
🔍 装饰有参数函数
def logger_decorator(func):
"""日志装饰器(支持有参数函数)"""
def wrapper(*args, **kwargs):
print(f"[LOG] 开始执行 {func.__name__}")
print(f"[LOG] 参数:args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"[LOG] 返回值:{result}")
print(f"[LOG] 执行完成 {func.__name__}")
return result
return wrapper
@logger_decorator
def add(a, b):
return a + b
@logger_decorator
def greet(name, greeting="你好"):
return f"{greeting}, {name}!"
# 调用
add(3, 5)
greet("小明")
greet("小红", greeting="Hello")
🔍 通用装饰器模板
def universal_decorator(func):
"""通用装饰器模板"""
def wrapper(*args, **kwargs):
# 前置处理
print("前置处理")
# 调用原函数
result = func(*args, **kwargs)
# 后置处理
print("后置处理")
# 返回结果
return result
return wrapper
7. 有参数装饰器
🔍 装饰器带参数
def repeat_decorator(times):
"""重复执行装饰器(带参数)"""
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for _ in range(times):
result = func(*args, **kwargs)
results.append(result)
return results
return wrapper
return decorator
# 使用
@repeat_decorator(3)
def say_hello():
return "Hello!"
result = say_hello()
print(result) # ['Hello!', 'Hello!', 'Hello!']
🔍 执行过程解析
┌─────────────────────────────────────────────────────────────┐
│ 带参数装饰器执行过程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ @repeat_decorator(3) │
│ def say_hello(): │
│ return "Hello!" │
│ │
│ 步骤 1: repeat_decorator(3) │
│ ↓ 返回 decorator 函数 │
│ 步骤 2: decorator(say_hello) │
│ ↓ 返回 wrapper 函数 │
│ 步骤 3: say_hello = wrapper │
│ │
│ 调用 say_hello() 时,实际调用 wrapper() │
│ │
└─────────────────────────────────────────────────────────────┘
🔍 多个参数的装饰器
def retry_decorator(max_attempts=3, delay=1):
"""重试装饰器"""
import time
def decorator(func):
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"尝试 {attempt + 1}/{max_attempts} 失败:{e}")
if attempt < max_attempts - 1:
time.sleep(delay)
raise Exception("所有尝试都失败了")
return wrapper
return decorator
# 使用
@retry_decorator(max_attempts=3, delay=0.5)
def risky_operation():
import random
if random.random() < 0.7:
raise Exception("随机失败")
return "成功!"
8. 多个装饰器叠加
🔍 装饰器叠加顺序
def decorator_a(func):
def wrapper(*args, **kwargs):
print("装饰器 A - 前")
result = func(*args, **kwargs)
print("装饰器 A - 后")
return result
return wrapper
def decorator_b(func):
def wrapper(*args, **kwargs):
print("装饰器 B - 前")
result = func(*args, **kwargs)
print("装饰器 B - 后")
return result
return wrapper
# 叠加使用
@decorator_a
@decorator_b
def my_function():
print("原函数执行")
return "结果"
# 调用
my_function()
🔍 执行顺序图解
┌─────────────────────────────────────────────────────────────┐
│ 多个装饰器执行顺序 │
├─────────────────────────────────────────────────────────────┤
│ │
│ @decorator_a │
│ @decorator_b │
│ def my_function(): │
│ pass │
│ │
│ 等价于:my_function = decorator_a(decorator_b(my_function))│
│ │
│ 执行顺序(从下到上装饰,从上到下执行): │
│ │
│ 装饰时:my_function → decorator_b → decorator_a │
│ │
│ 调用时: │
│ decorator_a 前 ← 最外层 │
│ decorator_b 前 │
│ 原函数执行 │
│ decorator_b 后 │
│ decorator_a 后 ← 最外层 │
│ │
│ 输出: │
│ 装饰器 A - 前 │
│ 装饰器 B - 前 │
│ 原函数执行 │
│ 装饰器 B - 后 │
│ 装饰器 A - 后 │
│ │
└─────────────────────────────────────────────────────────────┘
第三部分:装饰器进阶
9. 带参数的装饰器(进阶)
🔍 装饰器参数和函数参数
def prefix_decorator(prefix=""):
"""添加前缀的装饰器"""
def decorator(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return f"{prefix}{result}"
return wrapper
return decorator
# 使用
@prefix_decorator("[INFO] ")
def get_message():
return "操作成功"
@prefix_decorator("[ERROR] ")
def get_error():
return "操作失败"
print(get_message()) # [INFO] 操作成功
print(get_error()) # [ERROR] 操作失败
🔍 可选参数的装饰器
def conditional_decorator(condition=True):
"""条件装饰器"""
def decorator(func):
if condition:
def wrapper(*args, **kwargs):
print("条件满足,执行增强功能")
return func(*args, **kwargs)
return wrapper
else:
return func # 不装饰,直接返回原函数
return decorator
# 使用
@conditional_decorator(True)
def func1():
return "func1"
@conditional_decorator(False)
def func2():
return "func2"
print(func1()) # 有增强功能
print(func2()) # 无增强功能
10. 类装饰器
🔍 用类实现装饰器
class CountCalls:
"""统计函数调用次数的类装饰器"""
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"第 {self.count} 次调用 {self.func.__name__}")
return self.func(*args, **kwargs)
# 使用
@CountCalls
def say_hello():
print("Hello!")
say_hello()
say_hello()
say_hello()
print(f"总共调用了 {say_hello.count} 次")
🔍 带参数的类装饰器
class Retry:
"""重试类装饰器"""
def __init__(self, max_attempts=3):
self.max_attempts = max_attempts
def __call__(self, func):
def wrapper(*args, **kwargs):
for attempt in range(self.max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"尝试 {attempt + 1} 失败:{e}")
raise Exception("所有尝试都失败了")
return wrapper
# 使用
@Retry(max_attempts=3)
def risky_operation():
import random
if random.random() < 0.5:
raise Exception("随机失败")
return "成功!"
🔍 类装饰器 vs 函数装饰器
| 特性 | 函数装饰器 | 类装饰器 |
|---|---|---|
| 语法 | 简洁 | 稍复杂 |
| 状态管理 | 需要闭包 | 天然支持属性 |
| 可扩展性 | 一般 | 强(可继承) |
| 适用场景 | 简单装饰 | 复杂装饰 |
11. 装饰器中的 functools.wraps
⚠️ 问题:装饰器丢失原函数信息
def my_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@my_decorator
def original_function():
"""这是原函数的文档"""
pass
print(original_function.__name__) # wrapper ❌
print(original_function.__doc__) # None ❌
✅ 解决方案:使用 functools.wraps
from functools import wraps
def my_decorator(func):
@wraps(func) # 保留原函数信息
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@my_decorator
def original_function():
"""这是原函数的文档"""
pass
print(original_function.__name__) # original_function ✅
print(original_function.__doc__) # 这是原函数的文档 ✅
🔍 wraps 的作用
┌─────────────────────────────────────────────────────────────┐
│ functools.wraps 的作用 │
├─────────────────────────────────────────────────────────────┤
│ │
│ @wraps(func) 等价于手动复制以下属性: │
│ │
│ wrapper.__name__ = func.__name__ │
│ wrapper.__doc__ = func.__doc__ │
│ wrapper.__module__ = func.__module__ │
│ wrapper.__qualname__ = func.__qualname__ │
│ wrapper.__annotations__ = func.__annotations__ │
│ wrapper.__dict__.update(func.__dict__) │
│ │
│ 好处: │
│ • 调试时能看到原函数名 │
│ • 文档工具能正确提取文档 │
│ • IDE 能正确提示函数签名 │
│ │
└─────────────────────────────────────────────────────────────┘
12. 装饰器最佳实践
┌─────────────────────────────────────────────────────────────┐
│ 装饰器最佳实践 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ✅ 应该做的 │
│ • 始终使用 @wraps 保留原函数信息 │
│ • 使用 *args, **kwargs 保证通用性 │
│ • 装饰器函数名要有意义 │
│ • 添加适当的文档说明 │
│ • 处理异常情况 │
│ │
│ ❌ 不应该做的 │
│ • 不要修改原函数的行为(除非明确设计) │
│ • 不要过度嵌套装饰器 │
│ • 不要在装饰器中做耗时操作 │
│ • 不要依赖全局变量 │
│ │
│ 💡 建议 │
│ • 将常用装饰器放入单独模块 │
│ • 为装饰器编写测试用例 │
│ • 考虑装饰器的性能影响 │
│ │
└─────────────────────────────────────────────────────────────┘
🔍 最佳实践模板
from functools import wraps
def my_decorator(arg1, arg2=None):
"""装饰器说明文档
Args:
arg1: 参数 1 说明
arg2: 参数 2 说明
Returns:
装饰后的函数
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 前置处理
# ...
# 调用原函数
result = func(*args, **kwargs)
# 后置处理
# ...
return result
return wrapper
return decorator
第四部分:内置装饰器
13. @staticmethod 静态方法
📖 概念
静态方法 是不需要访问实例属性或类方法的方法,相当于普通函数放在类中。
🔍 使用示例
class MathUtils:
"""数学工具类"""
@staticmethod
def add(a, b):
"""加法"""
return a + b
@staticmethod
def multiply(a, b):
"""乘法"""
return a * b
@staticmethod
def is_even(n):
"""判断偶数"""
return n % 2 == 0
# 使用(不需要创建实例)
print(MathUtils.add(3, 5)) # 8
print(MathUtils.multiply(4, 6)) # 24
print(MathUtils.is_even(10)) # True
# 也可以通过实例调用
math = MathUtils()
print(math.add(3, 5)) # 8
🔍 静态方法 vs 实例方法
class MyClass:
def __init__(self, value):
self.value = value
# 实例方法:需要 self,访问实例属性
def instance_method(self):
return f"实例方法:{self.value}"
# 静态方法:不需要 self,不访问实例属性
@staticmethod
def static_method(x, y):
return x + y
obj = MyClass(10)
print(obj.instance_method()) # 实例方法:10
print(MyClass.static_method(3, 5)) # 8
14. @classmethod 类方法
📖 概念
类方法 第一个参数是类本身(cls),可以访问类属性,常用于工厂方法。
🔍 使用示例
class Person:
count = 0 # 类属性
def __init__(self, name):
self.name = name
Person.count += 1
@classmethod
def get_count(cls):
"""获取总人数"""
return cls.count
@classmethod
def from_string(cls, person_string):
"""从字符串创建 Person(工厂方法)"""
name = person_string.split("-")[0]
return cls(name)
# 使用
p1 = Person("小明")
p2 = Person.from_string("小红-25-北京")
print(Person.get_count()) # 2
print(p2.name) # 小红
🔍 类方法实际应用:日期解析
from datetime import date
class DateUtil:
@classmethod
def from_string(cls, date_string):
"""从字符串创建日期"""
year, month, day = map(int, date_string.split("-"))
return cls(year, month, day)
@classmethod
def today(cls):
"""获取今天"""
t = date.today()
return cls(t.year, t.month, t.day)
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def __str__(self):
return f"{self.year}-{self.month}-{self.day}"
# 使用
d1 = DateUtil.from_string("2026-03-26")
d2 = DateUtil.today()
print(d1) # 2026-3-26
print(d2) # 今天的日期
15. @property 属性装饰器
📖 概念
@property 将方法转换为属性,可以实现 getter、setter、deleter。
🔍 基本使用
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""getter"""
return self._radius
@radius.setter
def radius(self, value):
"""setter"""
if value < 0:
raise ValueError("半径不能为负数")
self._radius = value
@property
def area(self):
"""计算面积(只读属性)"""
return 3.14159 * self._radius ** 2
# 使用
circle = Circle(5)
print(circle.radius) # 5 (调用 getter)
circle.radius = 10 # 调用 setter
print(circle.area) # 314.159 (计算属性)
# circle.area = 100 # AttributeError: 只读属性
🔍 属性装饰器完整示例
class Employee:
def __init__(self, name, salary):
self._name = name
self._salary = salary
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not value:
raise ValueError("姓名不能为空")
self._name = value
@property
def salary(self):
return self._salary
@salary.setter
def salary(self, value):
if value < 0:
raise ValueError("工资不能为负数")
self._salary = value
@property
def annual_salary(self):
"""年薪(只读)"""
return self._salary * 12
@name.deleter
def name(self):
print("删除姓名")
del self._name
# 使用
emp = Employee("小明", 5000)
print(emp.name) # 小明
print(emp.annual_salary) # 60000
emp.salary = 6000 # 调用 setter
del emp.name # 调用 deleter
🔍 三种方法对比
| 装饰器 | 第一个参数 | 访问内容 | 调用方式 |
|---|---|---|---|
| 实例方法 | self | 实例属性 | obj.method() |
| @classmethod | cls | 类属性 | Class.method() |
| @staticmethod | 无 | 无 | Class.method() |
| @property | self | 实例属性 | obj.attr |
第五部分:综合实战练习
📌 重要说明:以下是练习题目,请读者先自行思考并完成代码,之后再查看单独发布的答案讲解博客进行核对!
16. 练习一:日志装饰器
📋 练习要求
创建一个功能完善的日志装饰器,实现以下功能:
-
log_decorator() - 基础日志装饰器
- 记录函数名
- 记录输入参数
- 记录返回值
- 记录执行时间
-
log_decorator(level="INFO") - 支持日志级别
- 支持 DEBUG、INFO、WARNING、ERROR 级别
- 可以设置最低日志级别
-
log_decorator(level="INFO", log_file="app.log") - 支持日志文件
- 可以将日志写入文件
- 支持同时输出到控制台和文件
💡 提示
- 使用 time 模块计算执行时间
- 使用 datetime 格式化时间
- 考虑异常情况的日志记录
📝 练习代码框架
# 请在此处完成你的代码
# 定义 log_decorator 函数
# 测试代码
if __name__ == "__main__":
print("=== 日志装饰器测试 ===")
@log_decorator()
def add(a, b):
return a + b
@log_decorator(level="DEBUG")
def divide(a, b):
return a / b
result = add(3, 5)
print(f"结果:{result}")
17. 练习二:计时装饰器
📋 练习要求
创建一个性能分析装饰器,实现以下功能:
-
timer_decorator() - 基础计时装饰器
- 记录函数执行时间
- 打印执行时间
-
timer_decorator(threshold=0.1) - 阈值警告
- 当执行时间超过阈值时发出警告
- 阈值可配置
-
timer_decorator(log_stats=True) - 统计功能
- 记录多次调用的统计数据
- 包括:调用次数、平均时间、最长时间、最短时间
-
@timer - 类装饰器版本
- 使用类实现计时装饰器
- 支持更复杂的状态管理
💡 提示
- 使用 time.perf_counter() 精确计时
- 使用字典存储统计数据
- 考虑线程安全问题
📝 练习代码框架
# 请在此处完成你的代码
# 定义 timer_decorator 函数
# 定义 Timer 类装饰器
# 测试代码
if __name__ == "__main__":
print("=== 计时装饰器测试 ===")
@timer_decorator(log_stats=True)
def slow_function():
import time
time.sleep(0.1)
return "完成"
for _ in range(5):
slow_function()
# 打印统计信息
# ...
18. 练习三:缓存装饰器
📋 练习要求
创建一个智能缓存装饰器,实现以下功能:
-
cache_decorator() - 基础缓存装饰器
- 缓存函数返回值
- 相同参数直接返回缓存
-
cache_decorator(max_size=128) - 缓存大小限制
- 限制缓存条目数量
- 超出时使用 LRU(最近最少使用)策略淘汰
-
cache_decorator(ttl=60) - 缓存过期时间
- 设置缓存有效期(秒)
- 过期后自动重新计算
-
cache_decorator(key_func=None) - 自定义缓存键
- 支持自定义缓存键生成函数
- 用于处理不可哈希参数
💡 提示
- 使用字典存储缓存
- 使用 time 模块检查过期
- 使用 OrderedDict 实现 LRU
📝 练习代码框架
# 请在此处完成你的代码
# 定义 cache_decorator 函数
# 测试代码
if __name__ == "__main__":
print("=== 缓存装饰器测试 ===")
@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)}")
# 打印缓存信息
# ...
📝 总结与下一篇预告
本篇核心知识点回顾
┌─────────────────────────────────────────────────────────────┐
│ 知识点思维导图 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 闭包和装饰器 │
│ │ │
│ ├── 闭包 │
│ │ ├── 概念和原理 │
│ │ ├── 创建条件 │
│ │ ├── 实际应用 │
│ │ └── 陷阱和解决方案 │
│ │ │
│ ├── 装饰器基础 │
│ │ ├── 概念和语法 │
│ │ ├── 无参数装饰器 │
│ │ ├── 有参数装饰器 │
│ │ └── 多个装饰器叠加 │
│ │ │
│ ├── 装饰器进阶 │
│ │ ├── 带参数的装饰器 │
│ │ ├── 类装饰器 │
│ │ ├── functools.wraps │
│ │ └── 最佳实践 │
│ │ │
│ ├── 内置装饰器 │
│ │ ├── @staticmethod │
│ │ ├── @classmethod │
│ │ └── @property │
│ │ │
│ └── 实战练习 │
│ ├── 日志装饰器 │
│ ├── 计时装饰器 │
│ └── 缓存装饰器 │
│ │
└─────────────────────────────────────────────────────────────┘
✅ 学习检查清单
- 理解闭包的概念和原理
- 掌握闭包的创建条件
- 能够识别和解决闭包陷阱
- 理解装饰器的本质和语法
- 能够编写无参数和有参数装饰器
- 掌握多个装饰器的叠加顺序
- 理解 functools.wraps 的作用
- 掌握内置装饰器的使用
- 完成 3 个实战练习题目
🚀 下一篇预告
《Python 零基础入门系列(十一):模块和包》
┌─────────────────────────────────────────────────────────────┐
│ 第十一篇内容预告 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1️⃣ 模块基础 │
│ • 模块的概念和导入 │
│ • import 语句详解 │
│ • from ... import 用法 │
│ │
│ 2️⃣ 模块进阶 │
│ • __name__ 和 __main__ │
│ • 模块搜索路径 │
│ • 模块的重新加载 │
│ │
│ 3️⃣ 包的概念 │
│ • 包的创建和结构 │
│ • __init__.py 的作用 │
│ • 包的导入方式 │
│ │
│ 4️⃣ 常用标准库模块 │
│ • os 模块 - 操作系统接口 │
│ • sys 模块 - 系统相关 │
│ • json 模块 - JSON 处理 │
│ • datetime 模块 - 日期时间 │
│ │
│ 5️⃣ 综合实战 │
│ • 创建自己的模块和包 │
│ • 发布和使用第三方包 │
│ │
└─────────────────────────────────────────────────────────────┘
📅 完整系列更新计划
┌─────────────────────────────────────────────────────────────┐
│ 🐍 Python 零基础入门系列 · 完整规划(14 篇版) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 📌 第一篇:Python 认知 + 环境搭建 + 基础语法 │
│ ✅ 已完成 │
│ │
│ 📌 第二篇:常用运算符完全详解 │
│ ✅ 已完成 │
│ │
│ 📌 第三篇:流程控制——条件判断与循环结构 │
│ ✅ 已完成 │
│ │
│ 📌 第四篇:字符串常用操作详解 │
│ ✅ 已完成 │
│ │
│ 📌 第五篇:列表与元组——序列操作全解析 │
│ ✅ 已完成 │
│ │
│ 📌 第六篇:字典与集合——映射与去重操作 │
│ ✅ 已完成 │
│ │
│ 📌 第七篇:函数基础知识(上) │
│ ✅ 已完成 │
│ │
│ 📌 第八篇:函数基础知识(下) │
│ ✅ 已完成 │
│ │
│ 📌 第九篇:函数进阶知识 │
│ ✅ 已完成 │
│ │
│ 📌 第九篇答案:函数进阶实战答案讲解 │
│ ✅ 已完成 │
│ │
│ 📌 第十篇(本篇):闭包和装饰器 │
│ ✅ 已完成 │
│ │
│ 📌 第十篇答案:闭包和装饰器实战答案讲解 │
│ 🔜 单独发布 │
│ │
│ 📌 第十一篇:模块和包 │
│ 🔜 敬请期待 │
│ │
│ 📌 第十二篇:文件操作详解 │
│ 🔜 敬请期待 │
│ │
│ 📌 第十三篇:异常处理 │
│ 🔜 敬请期待 │
│ │
│ 📌 第十四篇:综合实战项目 │
│ 🔜 敬请期待 │
│ │
└─────────────────────────────────────────────────────────────┘
💬 结语
"闭包和装饰器是 Python 高级编程的核心,掌握它们,让你的代码更加优雅、可复用!"
通过本篇学习,你已经:
- ✅ 理解了闭包的概念、原理和应用
- ✅ 掌握了装饰器的基础语法和使用
- ✅ 学会了编写带参数的装饰器
- ✅ 了解了类装饰器和 functools.wraps
- ✅ 掌握了内置装饰器 @staticmethod、@classmethod、@property
- ✅ 完成了 3 个实战练习题目
下一步建议:
- 完成本篇的 3 个实战练习
- 等待答案讲解博客发布,核对代码
- 预习下一篇:模块和包
- 尝试在实际项目中使用装饰器
记住:理解原理比记住语法更重要,多实践才能真正掌握!
📅 更新时间:2026 年 3 月 26 日
✍️ 作者:书到用时方恨少! | 专注 Python 教学与AI技术分享
💌 如有疑问,欢迎在评论区留言讨论!
如果这篇博客对你有帮助,请点赞👍、收藏⭐、转发🔄,让更多的小伙伴一起学习 Python!
更多推荐


所有评论(0)