文章目录
- 一、定义嵌套函数
- 二、定义闭包函数
- 三、闭包形成条件
- 四、何时使用闭包
- 五、闭包特性拾遗
本文将带你了解:
- 什么是 Python 闭包?
- 如何定义一个 Python 闭包?
- 为什么要使用 Python 的闭包?
一、定义嵌套函数
在了解什么是 Python 闭包之前,我们需要首先理解什么是嵌套函数。
- 嵌套函数:在一个函数(外部函数)内部定义的函数叫做嵌套函数。嵌套函数可以访问外部函数中定义的变量。
例如,下面的代码演示了在嵌套函数中访问一个非局部变量:
def print_msg(msg):
# 这是外部函数
def printer():
# 这是嵌套函数
print(msg)
printer()
# 执行函数,输出"Hello"
print_msg("Hello")
二、定义闭包函数
在上述例子中,如果更改 print_msg
函数的最后一行,将函数调用 printer
改为返回函数名 printer
会怎么样?请运行下列代码:
def print_msg(msg):
# 这是外部函数
def printer():
# 这是嵌套函数
print(msg)
return printer
foo = print_msg("Hello")
del print_msg
foo()
上述代码的运行结果依然是 "Hello"
,原因在于:
- 调用
print_msg
函数时传入参数"Hello"
; -
print_msg
函数的返回值赋给变量foo
,此时该变量指向printer
函数对象保存的空间; - 在函数
print_msg
执行完毕后,msg
变量依旧保存在内存中(使用del
关键字显式删除了该函数)。
于是:
闭包是 Python 的一个语言特性,该特性可以使得一些数据(msg)和代码(嵌套函数)绑定在一起。
三、闭包形成条件
由上述代码可知,在 Python 中,如果一个嵌套函数引用了一个定义在外部函数中的非局部变量,则就形成了一个闭包。
更严谨地,形成一个闭包需要以下三个条件:
- 需要有一个嵌套函数;
- 嵌套函数使用了定义在外部函数中的非局部变量;
- 外部函数的返回值必须是嵌套函数的名称。
四、何时使用闭包
闭包可以避免过多使用全局变量,并提供某种程度上的数据隐藏,同时,闭包还可以为某一问题提供类面向对象的解决方案。
最主要的是,有时,如果采用面向对象的方式实现某一需求,可能需实现的实例方法很少。此时,闭包就可以提供一个更加优雅、更节省内存(自定义一个类时,最少会继承object类中的方法,因而实例化自定义类时,所需内存较大)的解决方案。但是,当自定义类的属性和方法很多时,则最好还是定义一个类。
例如:现有这样一个需求,将任意一个数乘以指定的倍数。
(1)使用面向对象的方式:
class Multiplier(object):
def __init__(self, multiple):
self.multiple = multiple
# 当以调用callable的方式调用Multiplier的实例化对象时,
# __call__方法被自动调用
def __call__(self, *args, **kwargs):
print(args[0] * self.multiple)
multiplier3 = Multiplier(3)
multiplier3(5)
multiplier5 = Multiplier(5)
multiplier5(7)
(2)使用闭包的方式:
def make_multiplier_of(multiple):
def multiplier(num):
return num * multiple
return multiplier
# 3的倍数
times3 = make_multiplier_of(3)
# 5的倍数
times5 = make_multiplier_of(5)
print(times3(5))
print(times5(7))
print(times5(times3(2)))
五、闭包特性拾遗
事实上,所有的函数都有一个__closure__属性,当函数为闭包函数时,该属性为一个包含元胞对象(cell object)元组,否则为None,且通过每个元胞对象的cell_contents属性,可以获取存储在闭包中的非局部变量值,如下代码所示:
def foo_outer(argv):
var = 100
def foo_inner():
print(argv + var)
return foo_inner
foo = foo_outer(200)
foo()
print(foo_outer.__closure__)
print(foo.__closure__)
print("foo.__closure__[0].cell_contents = ", foo.__closure__[0].cell_contents)
print("foo.__closure__[1].cell_contents = ", foo.__closure__[1].cell_contents)
上述代码的运行结果为:
300
None
(<cell at 0x7f251a0fb438: int object at 0xa6a3a0>, <cell at 0x7f2518439078: int object at 0xa69720>)
foo.__closure__[0].cell_contents = 200
foo.__closure__[1].cell_contents = 100nonlocal关键字:一般来说,嵌套函数只可以访问在外部函数中定义的变量(称为非局部变量)而无法对其进行修改,如果确实需要对非局部变量进行修改,需要在嵌套函数中,对非局部变量使用nonlocal关键字进行显式声明。