一、定义
在 Python 中,当一个函数内部定义的函数引用了外部函数的局部变量时,就形成了一个闭包。这个内部函数可以访问并修改外部函数的局部变量,而这些局部变量的状态会一直被保存在闭包中,即使外部函数已经执行完毕。
这种机制使得闭包可以实现一些特殊的功能,例如记忆化(Memoization)和实现私有变量等。闭包可以在函数内部保存一些状态,并且这些状态对外部是不可见的,从而实现了一定程度上的信息隐藏和封装。
def outer_function():x = 10def inner_function():nonlocal x # 声明使用外部函数的局部变量 xx += 5return xreturn inner_function
closure = outer_function()
print(closure()) # 输出结果为 15
print(closure()) # 输出结果为 20
在这个示例中,inner_function 就是一个闭包,它引用了外部函数 outer_function 中的局部变量 x。每次调用 closure() 都会修改并返回 x 的值,而这个状态是被保存在闭包中的。
二、通过一个例子全面剖析一下闭包这个概念。
需求:实现银行系统的余额变化。
方式一:全局变量+函数
balance = 1000
def deposit(amount):global balancebalance += amountprint(f"成功存入 {amount} 元,当前余额为 {balance} 元")
def withdraw(amount):global balanceif amount <= balance:balance -= amountprint(f"成功取出 {amount} 元,当前余额为 {balance} 元")else:print("余额不足,取款失败")
def check_balance():print(f"当前余额为 {balance} 元")
# 存款和取款操作
deposit(500) # 存入 500 元
withdraw(200) # 取出 200 元
balance=10
deposit(500) # 存入 500 元
check_balance() # 查看余额
结果
成功存入 500 元,当前余额为 1500 元
成功取出 200 元,当前余额为 1300 元
成功存入 500 元,当前余额为 510 元
当前余额为 510 元
缺点:全局变量不安全,可以被随意访问和修改。
使用全局变量的方式虽然可以实现功能,但存在一些潜在问题:
-
可变性:全局变量的值是可变的,任何函数都可以直接修改它,这增加了程序出错的可能性,尤其在大型程序中更容易出现问题。
-
可见性:全局变量对整个程序都是可见的,这意味着任何部分都可以修改它,从而导致程序行为难以预测。
-
扩展性:如果需要管理多个账户,全局变量的方式就显得力不从心,因为很难将多个账户的信息独立地封装起来。
方案二:类
class BankAccount:def __init__(self, initial_balance):self.balance = initial_balance
def deposit(self, amount):self.balance += amountprint(f"成功存入 {amount} 元,当前余额为 {self.balance} 元")
def withdraw(self, amount):if amount <= self.balance:self.balance -= amountprint(f"成功取出 {amount} 元,当前余额为 {self.balance} 元")else:print("余额不足,取款失败")
def check_balance(self):print(f"当前余额为 {self.balance} 元")
# 创建账户
account = BankAccount(1000)
account.balance=550
# 存款和取款操作
account.deposit(500) # 存入 500 元
account.withdraw(200) # 取出 200 元
account.check_balance() # 查看余额
结果
成功存入 500 元,当前余额为 1050 元
成功取出 200 元,当前余额为 850 元
当前余额为 850 元
缺点:共有属性也能被对象访问修改,不安全。
方案三:类+私有属性
class BankAccount:def __init__(self, initial_balance):self.__balance = initial_balance
def deposit(self, amount):self.__balance += amountprint(f"成功存入 {amount} 元,当前余额为 {self.__balance} 元")
def withdraw(self, amount):if amount <= self.__balance:self.__balance -= amountprint(f"成功取出 {amount} 元,当前余额为 {self.__balance} 元")else:print("余额不足,取款失败")
def check_balance(self):print(f"当前余额为 {self.__balance} 元")
account = BankAccount(1000)
account.deposit(500)
account.withdraw(200)
account.check_balance()
结果:
成功存入 500 元,当前余额为 1500 元
成功取出 200 元,当前余额为 1300 元
当前余额为 1300 元
问题得到解决。
方案四:闭包
def create_account(initial_balance):balance = initial_balancedef deposit(amount):nonlocal balancebalance += amountprint(f"成功存入 {amount} 元,当前余额为 {balance} 元")
def withdraw(amount):nonlocal balanceif amount <= balance:balance -= amountprint(f"成功取出 {amount} 元,当前余额为 {balance} 元")else:print("余额不足,取款失败")
def check_balance():print(f"当前余额为 {balance} 元")
return deposit, withdraw, check_balance
# 创建账户
deposit, withdraw, check_balance = create_account(1000)
# 存款和取款操作
deposit(500) # 存入 500 元
print(deposit)
withdraw(200) # 取出 200 元
check_balance() # 查看余额
# 创建账户
deposit1, withdraw1, check_balance1 = create_account(10000)
# 存款和取款操作
deposit1(500) # 存入 500 元
print(deposit1)
withdraw1(200) # 取出 200 元
check_balance1() # 查看余额
结果
成功存入 500 元,当前余额为 1500 元
<function create_account.<locals>.deposit at 0x0000020DCC711990>
成功取出 200 元,当前余额为 1300 元
当前余额为 1300 元
成功存入 500 元,当前余额为 10500 元
<function create_account.<locals>.deposit at 0x0000020DCC711A20>
成功取出 200 元,当前余额为 10300 元
当前余额为 10300 元
完美解决了问题
三、辨析
闭包和类是两种不同的概念,它们在编程中有着不同的用途和特点。
闭包(Closure)是指可以在其词法作用域之外执行的函数,但仍然保持对其作用域内变量的引用。换句话说,闭包是函数及其相关的引用环境的组合。闭包可以用来封装状态、实现私有变量等功能。在 Python 中,当一个函数内部定义的函数引用了外部函数的局部变量时,就形成了一个闭包。
类(Class)则是面向对象编程中的重要概念,它用来描述具有相似属性和行为的对象的模板。类由属性(成员变量)和方法(成员函数)组成,可以通过实例化来创建对象,并且支持继承、多态等面向对象的特性。类的主要作用是封装数据和操作数据的方法,以及实现代码复用和抽象。
下面是闭包和类的一些区别:
-
封装方式不同:闭包是一种函数式编程的封装方式,通过函数和其引用环境来封装状态和行为;类是一种面向对象编程的封装方式,通过属性和方法来封装数据和操作。
-
状态的保存方式不同:闭包通过引用环境来保存状态,而类通过实例变量和类变量来保存状态。
-
范围不同:闭包通常用于封装一些局部状态,提供函数式编程的功能;类则通常用于描述对象的行为和属性,提供面向对象编程的特性。
总的来说,闭包和类都是用于封装和抽象的工具,但其应用场景和实现方式有所不同。在实际编程中,可以根据具体的需求和问题选择合适的工具来实现相应的功能。