Python 闭包详解:原理、创建条件与实际应用场景
本文深入解析 Python 闭包的创建原理与使用方法。通过嵌套函数与非局部变量的概念,阐述闭包如何在不使用全局变量的情况下实现数据隐藏。文章详细列出创建闭包的三个必要条件,并探讨其在类方法较少时的优雅替代方案,以及作为 Python 装饰器核心机制的重要应用。
Python 闭包的使用方法
嵌套函数中的非局部变量
在深入探讨闭包之前,需要先理解嵌套函数和非局部变量这两个核心概念。
嵌套函数是指在另一个函数内部定义的函数。这种内部函数有权访问其外层函数作用域内的变量。
在 Python 中,默认情况下这些非局部变量只能被读取。若要在嵌套函数中修改外层函数的变量,必须使用 `nonlocal` 关键字显式声明。
以下是访问非局部变量的嵌套函数示例。
def print_msg(msg):
# This is the outer enclosing function
def printer():
# This is the nested function
print(msg)
printer()
# We execute the function
# Output: Hello
print_msg("Hello")
从代码可以看出,嵌套函数 `printer()` 成功访问了外层函数 `print_msg()` 中的非局部变量 `msg`。
定义闭包函数
如果修改上述示例,让 `print_msg()` 函数最后一行返回 `printer` 函数对象本身而不是调用它,结果会如何?定义方式如下所示:
def print_msg(msg):
# This is the outer enclosing function
def printer():
# This is the nested function
print(msg)
return printer # this got changed
# Now let's try calling this function.
# Output: Hello
another = print_msg("Hello")
another()
这种写法看似不寻常,但揭示了闭包的本质。
当使用字符串“Hello”调用 `print_msg()` 时,返回的函数被赋值给另一个变量。即使 `print_msg()` 的执行已经结束,调用该返回函数(如 `another()`)时,它依然能记住并访问当初传入的消息。
这种将数据(如“Hello”)附加到代码执行环境中的技术,在 Python 中被称为闭包。
即使外层变量已经超出作用范围,或者外层函数本身已从当前命名空间中删除,闭包仍能保留并访问其封闭范围内的值。
你可以在 Python 交互环境中尝试以下代码以验证输出结果。
>>> del print_msg
>>> another()
Hello
>>> print_msg("Hello")
Traceback (most recent call last):
...
NameError: name 'print_msg' is not defined
什么时候形成闭包?
通过上述示例可以看出,当嵌套函数引用了其封闭作用域内的值时,Python 便形成了一个闭包。
在 Python 中创建有效的闭包必须满足以下三个标准:
- 必须存在一个嵌套函数(即函数内部定义的函数)。
- 嵌套函数必须引用封闭函数中定义的值(非局部变量)。
- 闭包函数必须返回该嵌套函数。
何时使用闭包?
那么,闭包的优势究竟在哪里?
闭包能够有效避免使用全局变量,并提供一定程度的数据隐藏。此外,它还能提供一种面向对象的解决方案。
当类中仅包含极少的方法(通常只有一种方法)时,使用闭包可以提供一个更简洁、优雅的替代方案。然而,如果类的属性和方法数量较多,则实现一个完整的类更为合适。
这是一个简单的示例,展示了在特定场景下闭包比定义类和创建对象更具优先级的情况。
def make_multiplier_of(n):
def multiplier(x):
return x * n
return multiplier
# Multiplier of 3
times3 = make_multiplier_of(3)
# Multiplier of 5
times5 = make_multiplier_of(5)
# Output: 27
print(times3(9))
# Output: 15
print(times5(3))
# Output: 30
print(times5(times3(2)))
Python 中的装饰器也广泛使用了闭包机制。值得注意的是,装饰器内部可以访问封闭函数中包含的值。
所有函数对象都拥有一个 `__closure__` 属性。如果该函数是闭包,此属性将返回一个包含单元格对象(cell object)的元组。参考前文示例,我们知道 `times3` 和 `times5` 均为闭包函数。
>>> make_multiplier_of.__closure__ >>> times3.__closure__ (<cell at 0x0000000002D155B8: int object at 0x000000001E39B6E0>,)
单元格(cell)对象具有一个名为 `cell_contents` 的属性,用于存储闭包中的值。
>>> times3.__closure__[0].cell_contents 3 >>> times5.__closure__[0].cell_contents 5
如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









