关于闭包
闭包(closure)的概念多见于javascript语言中,实际上,python语言中也存在闭包的概念,但是往往没有被注意到,实际上,在定义一个装饰器的时候,就用到了闭包的概念。
关于闭包,实现起来只需要注意三点就好:
- 嵌套函数;
- 内部函数用到了外部变量;
- 外部函数返回内部函数;
关于闭包的概念就像上述这么简单,但是该怎么实现一个闭包函数呢?接着往下看。
实现闭包函数
这是一个简单的示例:
1 | def (x): |
这是一个简单的闭包函数,但是已经符合上面提及到的条件。
- 在外层函数
compute
中嵌套了一个内层函数echo
; - 内层函数的返回值中调用到外层函数的参数
x
; - 外部函数
compute
返回内层函数echo
;
那么我们直接调用外层函数,输出结果是什么呢?
1 | def (x): |
输出结果是:
1 | <function compute.<locals>.echo at 0x107d1a730> |
看得出来,虽然内层函数没有传递参数,但是结果并没有报错,返回的是一个函数对象,这也符合上述第三点所说的,外部函数返回内部函数这一个条件。
那么怎么才能使用这么闭包函数呢?既然我们知道了外部函数返回的是内部函数,那么按照python中一切皆对象的哲学,可以试一试:
1 | func = compute(2) |
这一次,有输出结果6
,也符合我们函数的逻辑返回x=2,value=3
的值。这一步,我们实现了一个简单的闭包函数。
关于闭包函数
闭包函数的参数
首先,我们讨论一下闭包函数的变量作用域,看下面的一个示例:
1 | def foo(x): |
这个示例的输出是14
,而不是24 大专栏 python闭包
。造成这个结果的原因是:闭包函数中传入的参数是在定义的时候确定的,而不是在调用的时候,即print(func(4))
这行代码在运行的时候,其中传入的参数b
是在函数foo
定义的时候传入的参数5
。
关于__closure__
在python中,函数也是对象,那么函数也有属性,其中和闭包相关的属性就是__closure__
。__closure__
属性定义了一组包含cell
对象的元组,该元组中的每一个cell
用来保存作用域中变量的值。
1 | def foo(x): |
上述代码输出的结果如下:
1 | (<cell at 0x10f9bf078: int object at 0x10f7b3ac0>, <cell at 0x10f9bf1c8: int object at 0x10f7b3a60>) |
其中的含义分别是,__closure__
属性中存在两个cell
,则说明在作用域中有两个变量值的存在,print(len(foo.__closure__))
的返回值是2
也说明了这一点。两个变量值分别是5
和2
,对应的也就是定义中的b
的值和func=foo(2)
中传入的参数值。
闭包的使用
接触过python的人都知道,python语言中有装饰器这个语法糖,实际上,编写一个装饰器的时候,就用到了闭包相关的概念。例如,《Python Cookbook》中的一个示例:
1 | import time |
主体函数中,使用的就是闭包的概念。理解了闭包的概念,对于装饰器的理解会更好一些,后续在关于装饰器的使用给出一些总结。