Python3系列:闭包和装饰器

一、闭包基础概念

1.1 什么是闭包?

闭包是指内部函数引用了外部函数的变量,并且外部函数返回内部函数的一种编程结构。

# 1.1.1 最简单的闭包
def outer_function(msg):
    """外部函数"""
    message = msg  # 外部函数的变量
    
    def inner_function():
        """内部函数 - 闭包"""
        print(message)  # 引用了外部变量
    
    return inner_function  # 返回内部函数

# 创建闭包
hello_func = outer_function("Hello World!")
hello_func()  # 输出: Hello World!

# 创建不同的闭包
hi_func = outer_function("Hi there!")
hi_func()  # 输出: Hi there!

# 每个闭包都记住了自己的环境
hello_func()  # 仍然输出: Hello World!

 

1.2 闭包的特点

def make_counter():
    """创建一个计数器"""
    count = 0  # 外部函数的变量
    
    def counter():
        nonlocal count  # 声明使用外部变量
        count += 1
        return count
    
    return counter

# 创建两个独立的计数器
counter1 = make_counter()
counter2 = make_counter()

print(counter1())  # 1
print(counter1())  # 2
print(counter1())  # 3

print(counter2())  # 1 (独立的,从0开始)
print(counter2())  # 2

# 验证闭包的特性
print(counter1.__closure__)  # 显示闭包中引用的变量
print(counter1.__closure__[0].cell_contents)  # 访问闭包变量的值

  

二、闭包的核心机制

2.1 变量的绑定与生命周期

def power_factory(exponent):
    """创建幂函数的工厂"""
    # exponent 被绑定在闭包中
    
    def power(base):
        return base ** exponent
    
    return power

# 创建不同的幂函数
square = power_factory(2)     # 平方函数
cube = power_factory(3)       # 立方函数
sqrt = power_factory(0.5)     # 平方根函数

print(square(5))   # 25
print(cube(5))     # 125
print(sqrt(25))    # 5.0

# exponent 变量在外部函数返回后仍然存在
print(square.__closure__[0].cell_contents)  # 2
print(cube.__closure__[0].cell_contents)    # 3

2.2 nonlocal 关键字详解

def make_advanced_counter():
    """演示 nonlocal 的必要性"""
    count = 0
    total = 0
    
    def increment(amount=1):
        nonlocal count, total  # 声明这些变量来自外部作用域
        count += amount
        total += amount
        return count
    
    def decrement(amount=1):
        nonlocal count, total
        count -= amount
        total += amount  # 绝对值累加
        return count
    
    def get_stats():
        return {
            'current': count,
            'total_operations': total,
            'average': total / (count + total) if count else 0
        }
    
    # 返回多个内部函数
    return {
        'inc': increment,
        'dec': decrement,
        'stats': get_stats
    }

# 使用
counter = make_advanced_counter()
print(counter['inc'](5))   # 5
print(counter['inc'](3))   # 8
print(counter['dec'](2))   # 6
print(counter['stats']())  # {'current': 6, 'total_operations': 10, 'average': 0.625}

  

三、闭包的常见应用场景

3.1 数据隐藏与封装

def create_bank_account(initial_balance=0):
    """创建银行账户(闭包实现私有数据balance的访问)"""
    balance = initial_balance
    transactions = []
    
    def deposit(amount):
        nonlocal balance
        if amount <= 0:
            raise ValueError("存款金额必须为正数")
        balance += amount
        transactions.append(('deposit', amount, balance))
        return balance
    
    def withdraw(amount):
        nonlocal balance
        if amount <= 0:
            raise ValueError("取款金额必须为正数")
        if amount > balance:
            raise ValueError("余额不足")
        balance -= amount
        transactions.append(('withdraw', amount, balance))
        return balance
    
    def get_balance():
        return balance
    
    def get_history():
        return transactions.copy()
    
    def reset():
        nonlocal balance
        balance = 0
        transactions.clear()
    
    # 返回操作接口
    return {
        'deposit': deposit,
        'withdraw': withdraw,
        'balance': get_balance,
        'history': get_history,
        'reset': reset
    }

# 使用账户
account = create_bank_account(1000)
account['deposit'](500)
account['withdraw'](200)
print(account['balance']())  # 1300
print(account['history']())  # [('deposit', 500, 1500), ('withdraw', 200, 1300)]

# 无法直接访问私有数据
# print(account.balance)  # 错误!
# print(account.transactions)  # 错误!

  

3.2 装饰器实现

装饰器的本质定义

装饰器:接受一个函数作为参数,并返回一个新的函数(或修改后的原函数)的可调用对象。

import time
from functools import wraps

def timer_decorator(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

def retry_decorator(max_retries=3, delay=1):
    """带参数的重试装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_retries - 1:
                        raise
                    print(f"第 {attempt + 1} 次失败,重试中...")
                    time.sleep(delay)
            return None
        return wrapper
    return decorator

@timer_decorator
def slow_function():
    time.sleep(0.5)
    return "完成"

@retry_decorator(max_retries=3, delay=0.5)
def unstable_function():
    import random
    if random.random() < 0.7:  # 70% 概率失败
        raise ValueError("随机失败")
    return "成功"

# 测试
slow_function()
print(unstable_function())

  

使用 @wraps 的铁律:

  • ✅ 当你的装饰器创建了新的包装函数时,必须用 @wraps

  • ❌ 当你的装饰器直接返回原函数时,不需要 @wraps

# 需要 @wraps
def decorator(func): @wraps(func) def wrapper(*args, **kwargs): # ⭐ 创建了新函数 return func(*args, **kwargs) return wrapper # 不需要 @wraps def decorator(func): print(f"注册: {func.__name__}") # ⭐ 没创建新函数 return func

  

posted @ 2026-03-09 18:24  sunshine_coast  阅读(1)  评论(0)    收藏  举报