🐍 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. 练习一:日志装饰器

📋 练习要求

创建一个功能完善的日志装饰器,实现以下功能:

  1. log_decorator() - 基础日志装饰器

    • 记录函数名
    • 记录输入参数
    • 记录返回值
    • 记录执行时间
  2. log_decorator(level="INFO") - 支持日志级别

    • 支持 DEBUG、INFO、WARNING、ERROR 级别
    • 可以设置最低日志级别
  3. 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. 练习二:计时装饰器

📋 练习要求

创建一个性能分析装饰器,实现以下功能:

  1. timer_decorator() - 基础计时装饰器

    • 记录函数执行时间
    • 打印执行时间
  2. timer_decorator(threshold=0.1) - 阈值警告

    • 当执行时间超过阈值时发出警告
    • 阈值可配置
  3. timer_decorator(log_stats=True) - 统计功能

    • 记录多次调用的统计数据
    • 包括:调用次数、平均时间、最长时间、最短时间
  4. @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. 练习三:缓存装饰器

📋 练习要求

创建一个智能缓存装饰器,实现以下功能:

  1. cache_decorator() - 基础缓存装饰器

    • 缓存函数返回值
    • 相同参数直接返回缓存
  2. cache_decorator(max_size=128) - 缓存大小限制

    • 限制缓存条目数量
    • 超出时使用 LRU(最近最少使用)策略淘汰
  3. cache_decorator(ttl=60) - 缓存过期时间

    • 设置缓存有效期(秒)
    • 过期后自动重新计算
  4. 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 个实战练习题目

下一步建议

  1. 完成本篇的 3 个实战练习
  2. 等待答案讲解博客发布,核对代码
  3. 预习下一篇:模块和包
  4. 尝试在实际项目中使用装饰器

记住理解原理比记住语法更重要,多实践才能真正掌握!


📅 更新时间:2026 年 3 月 26 日
✍️ 作者:书到用时方恨少! | 专注 Python 教学与AI技术分享
💌 如有疑问,欢迎在评论区留言讨论!

如果这篇博客对你有帮助,请点赞👍、收藏⭐、转发🔄,让更多的小伙伴一起学习 Python!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐