Python 中的闭包详解
闭包的基本概念
闭包是 Python 中一个重要的概念,它是指一个函数对象,即使在其定义作用域之外执行时,仍然能够访问其定义作用域中的变量。
闭包的基本结构
pythondef outer_function(x): """外部函数""" def inner_function(y): """内部函数""" return x + y return inner_function # 创建闭包 closure = outer_function(10) # 调用闭包 print(closure(5)) # 15 print(closure(20)) # 30
闭包的三个条件
- 必须有一个嵌套函数(内部函数)
- 内部函数必须引用外部函数中的变量
- 外部函数必须返回这个内部函数
pythondef make_multiplier(factor): """创建乘法闭包""" def multiply(number): return number * factor return multiply # 创建不同的乘法器 double = make_multiplier(2) triple = make_multiplier(3) print(double(5)) # 10 print(triple(5)) # 15
闭包的工作原理
变量的作用域
pythondef outer(): x = 10 def inner(): # 内部函数可以访问外部函数的变量 print(f"内部函数访问 x: {x}") return x return inner closure = outer() print(closure()) # 内部函数访问 x: 10, 10
变量的生命周期
pythondef counter(): """计数器闭包""" count = 0 def increment(): nonlocal count count += 1 return count return increment # 创建计数器 my_counter = counter() print(my_counter()) # 1 print(my_counter()) # 2 print(my_counter()) # 3 # 创建另一个计数器 another_counter = counter() print(another_counter()) # 1
closure 属性
pythondef outer(x): def inner(y): return x + y return inner closure = outer(10) # 查看闭包的变量 print(closure.__closure__) # (<cell at 0x...: int object at 0x...>,) print(closure.__closure__[0].cell_contents) # 10
闭包的实际应用
1. 数据隐藏和封装
pythondef make_account(initial_balance): """创建银行账户""" balance = initial_balance def deposit(amount): nonlocal balance balance += amount return balance def withdraw(amount): nonlocal balance if amount <= balance: balance -= amount return balance else: raise ValueError("余额不足") def get_balance(): return balance # 返回多个函数 return { 'deposit': deposit, 'withdraw': withdraw, 'get_balance': get_balance } # 创建账户 account = make_account(100) # 使用账户 print(account['deposit'](50)) # 150 print(account['withdraw'](30)) # 120 print(account['get_balance']()) # 120 # balance 变量被隐藏,无法直接访问 # print(balance) # NameError: name 'balance' is not defined
2. 函数工厂
pythondef make_power_function(power): """创建幂函数""" def power_function(base): return base ** power return power_function # 创建不同的幂函数 square = make_power_function(2) cube = make_power_function(3) fourth_power = make_power_function(4) print(square(3)) # 9 print(cube(3)) # 27 print(fourth_power(3)) # 81
3. 延迟计算
pythondef lazy_sum(*args): """延迟求和""" def sum(): total = 0 for num in args: total += num return total return sum # 创建延迟求和函数 f = lazy_sum(1, 2, 3, 4, 5) # 调用时才计算 print(f()) # 15
4. 缓存和记忆化
pythondef memoize(func): """记忆化装饰器""" cache = {} def memoized(*args): if args not in cache: cache[args] = func(*args) return cache[args] return memoized @memoize def fibonacci(n): """斐波那契数列""" if n < 2: return n return fibonacci(n-1) + fibonacci(n-2) print(fibonacci(10)) # 55 print(fibonacci(20)) # 6765
5. 回调函数
pythondef make_callback(callback): """创建回调函数""" def execute(*args, **kwargs): print("执行回调前...") result = callback(*args, **kwargs) print("执行回调后...") return result return execute def my_function(x, y): return x + y # 创建带回调的函数 callback_function = make_callback(my_function) print(callback_function(3, 5)) # 执行回调前..., 8, 执行回调后...
6. 状态保持
pythondef make_state_machine(): """创建状态机""" state = 'idle' def transition(action): nonlocal state print(f"当前状态: {state}, 动作: {action}") if state == 'idle': if action == 'start': state = 'running' elif state == 'running': if action == 'pause': state = 'paused' elif action == 'stop': state = 'idle' elif state == 'paused': if action == 'resume': state = 'running' elif action == 'stop': state = 'idle' print(f"新状态: {state}") return state return transition # 创建状态机 state_machine = make_state_machine() state_machine('start') # idle -> running state_machine('pause') # running -> paused state_machine('resume') # paused -> running state_machine('stop') # running -> idle
闭包与装饰器
闭包实现装饰器
pythondef my_decorator(func): """简单的装饰器""" def wrapper(): print("装饰器:函数调用前") result = func() print("装饰器:函数调用后") return result return wrapper @my_decorator def say_hello(): print("Hello!") say_hello() # 输出: # 装饰器:函数调用前 # Hello! # 装饰器:函数调用后
带参数的装饰器
pythondef repeat(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(3) def greet(name): return f"Hello, {name}!" print(greet("Alice")) # 输出: ['Hello, Alice!', 'Hello, Alice!', 'Hello, Alice!']
闭包的注意事项
1. 循环变量的陷阱
python# 错误的做法 def create_multipliers(): return [lambda x: x * i for i in range(5)] multipliers = create_multipliers() print([m(2) for m in multipliers]) # [8, 8, 8, 8, 8] - 错误! # 正确的做法 - 使用默认参数 def create_multipliers_correct(): return [lambda x, i=i: x * i for i in range(5)] multipliers_correct = create_multipliers_correct() print([m(2) for m in multipliers_correct]) # [0, 2, 4, 6, 8] - 正确
2. 修改外部变量
pythondef outer(): count = 0 def increment(): nonlocal count # 必须使用 nonlocal 关键字 count += 1 return count return increment counter = outer() print(counter()) # 1 print(counter()) # 2
3. 内存泄漏风险
pythondef large_closure(): """创建大闭包""" large_data = list(range(1000000)) def process(): return sum(large_data[:100]) return process # 闭包会保持对 large_data 的引用 # 即使只使用其中的一小部分 closure = large_closure() # 如果不再需要闭包,应该删除引用 del closure
闭包 vs 类
闭包实现
pythondef make_counter(): """使用闭包实现计数器""" count = 0 def increment(): nonlocal count count += 1 return count def get_count(): return count return { 'increment': increment, 'get_count': get_count } counter = make_counter() print(counter['increment']()) # 1 print(counter['increment']()) # 2 print(counter['get_count']()) # 2
类实现
pythonclass Counter: """使用类实现计数器""" def __init__(self): self.count = 0 def increment(self): self.count += 1 return self.count def get_count(self): return self.count counter = Counter() print(counter.increment()) # 1 print(counter.increment()) # 2 print(counter.get_count()) # 2
何时使用闭包 vs 类
python# 使用闭包的场景: # 1. 简单的状态保持 def make_accumulator(): total = 0 def add(value): nonlocal total total += value return total return add # 2. 函数工厂 def make_power(power): def power_function(base): return base ** power return power_function # 使用类的场景: # 1. 复杂的状态管理 class BankAccount: def __init__(self, initial_balance): self.balance = initial_balance self.transactions = [] def deposit(self, amount): self.balance += amount self.transactions.append(('deposit', amount)) def withdraw(self, amount): if amount <= self.balance: self.balance -= amount self.transactions.append(('withdraw', amount)) def get_balance(self): return self.balance def get_transactions(self): return self.transactions # 2. 需要多个方法和属性 class Calculator: def __init__(self): self.history = [] def add(self, a, b): result = a + b self.history.append(f"{a} + {b} = {result}") return result def subtract(self, a, b): result = a - b self.history.append(f"{a} - {b} = {result}") return result def get_history(self): return self.history
闭包的高级应用
1. 部分函数应用
pythondef partial(func, *args, **kwargs): """部分函数应用""" def wrapper(*more_args, **more_kwargs): all_args = args + more_args all_kwargs = {**kwargs, **more_kwargs} return func(*all_args, **all_kwargs) return wrapper def power(base, exponent): return base ** exponent square = partial(power, exponent=2) cube = partial(power, exponent=3) print(square(5)) # 25 print(cube(5)) # 125
2. 函数组合
pythondef compose(*functions): """函数组合""" def wrapper(arg): result = arg for func in reversed(functions): result = func(result) return result return wrapper def add_one(x): return x + 1 def multiply_two(x): return x * 2 def square(x): return x ** 2 # 组合函数 combined = compose(square, multiply_two, add_one) print(combined(3)) # ((3 + 1) * 2) ** 2 = 64
3. 验证器
pythondef make_validator(validator_func, error_message): """创建验证器""" def validate(value): if not validator_func(value): raise ValueError(error_message) return value return validate # 创建验证器 is_positive = make_validator( lambda x: x > 0, "值必须为正数" ) is_email = make_validator( lambda x: '@' in x and '.' in x, "无效的邮箱地址" ) # 使用验证器 print(is_positive(10)) # 10 # is_positive(-5) # ValueError: 值必须为正数 print(is_email("user@example.com")) # user@example.com # is_email("invalid") # ValueError: 无效的邮箱地址
4. 限流器
pythonimport time def rate_limiter(max_calls, time_window): """创建限流器""" calls = [] def limiter(func): def wrapper(*args, **kwargs): current_time = time.time() # 移除超出时间窗口的调用记录 calls[:] = [call_time for call_time in calls if current_time - call_time < time_window] # 检查是否超过限制 if len(calls) >= max_calls: raise Exception(f"超过限流限制:{max_calls} 次/{time_window} 秒") # 记录调用 calls.append(current_time) # 执行函数 return func(*args, **kwargs) return wrapper return limiter @rate_limiter(max_calls=3, time_window=1) def api_call(): print("API 调用成功") return "success" # 测试限流 api_call() # 成功 api_call() # 成功 api_call() # 成功 # api_call() # Exception: 超过限流限制:3 次/1 秒
总结
Python 闭包的核心概念:
- 基本定义:闭包是一个函数对象,能够访问其定义作用域中的变量
- 三个条件:嵌套函数、引用外部变量、返回内部函数
- 工作原理:通过
__closure__属性保持对外部变量的引用
闭包的实际应用:
- 数据隐藏和封装
- 函数工厂
- 延迟计算
- 缓存和记忆化
- 回调函数
- 状态保持
闭包的注意事项:
- 循环变量的陷阱
- 使用
nonlocal修改外部变量 - 注意内存泄漏风险
闭包 vs 类:
- 闭包:适合简单的状态保持和函数工厂
- 类:适合复杂的状态管理和多个方法
闭包的高级应用:
- 部分函数应用
- 函数组合
- 验证器
- 限流器
闭包是 Python 中一个强大而优雅的特性,它允许函数保持状态,实现数据隐藏,并创建更加灵活和可重用的代码。掌握闭包对于编写高质量的 Python 代码非常重要。