闭包并不只是一个Python中的概念,在函数式编程语言中应用较为广泛。理解Python中的闭包一方面是能够正确的使用闭包,另一方面可以好好体会和思考闭包的设计思想。

要理解Python闭包,先要了解Python中的变量作用域规则。

变量作用域规则

首先,在函数中是能访问全局变量的:

[linuxmi@linux:~]$ python3
Python 3.6.9 (default, Nov  7 2019, 10:44:02)  
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for  more information.
>>> i = 'www.linuxmi.com'
>>> def  foo():
...      print(i)
... 
>>>  foo()
www.linuxmi.com
>>>

然后,在一个嵌套函数中,内层函数能够访问在外层函数中定义的局部变量:

>>> def foo():
...     i = 'linuxmi.com'
...     def  bar():
...         print(i)
...     return bar
... 
>>>  foo()()
linuxmi.com

闭包

上面的嵌套函数就是闭包。闭包是指延伸了作用域的函数,在其中能够访问未在函数定义体中定义的非全局变量。未在函数定义体中定义的非全局变量一般都是在嵌套函数中出现的。

上述示例中的变量i就是一个并未在函数bar中定义的非全局变量。对于bar来说,它有个专业名字,叫做自由变量

自由变量的名称可以在字节码对象中查看:

>>> bar = foo()
>>> bar.__code__.co_freevars
('i',)

自由变量的值绑定在函数的__closure__属性中:

>>> bar.__closure__
(<cell at 0x7fc04b2ae288: str object at  0x7fc0472efa30>,)

其中保存了对应自由变量的cell对象的序列,cell对象的cell_contents属性保存了变量的值:

>>> bar.__closure__[0].cell_contents
'linuxmi.com'

闭包的格式

下面用伪代码进行闭包格式的描述

def 外层函数(参数):
    def 内层函数():
        print("内层函数执行", 参数)

    return 内层函数

内层函数的引用 = 外层函数("传入参数")
内层函数的引用()

外层函数中的参数,不一定要有,据情况而定,但是一般情况下都会有并在内函数中使用到

案例

def func(x, y):
    def line(z):
        return x * y - z
    return line
line = func(8, 9)
print(line(10))

结果得到 62

在这个案例中,外函数func有接收参数 x=8,y=9,内函数line接收参数z=10,在内函数体中计算了a*x-b 即 8×9-10的值作为返回值,外函数返回内函数的引用,这里的引用指的是内函数line在内存中的起始地址,最终调用内函数line()得到返回值62

闭包的用途

Python中,闭包的主要用途就是用于装饰器的实现。