closure
闭包概念
闭包(closure)是函数式编程的重要的语法结构。内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包。
Python以函数对象为基础,为闭包这一语法结构提供支持的 (我们在特殊方法与多范式中,已经多次看到Python使用对象来实现一些特殊的语法)。Python一切皆对象,函数这一语法结构也是一个对象。在函数对象中,我们像使用一个普通对象一样使用函数对象,比如更改函数对象的名字,或者将函数对象作为参数进行传递。
def line_conf():
def line(x):
return 2*x+1
print(line(5)) # within the scope
line_conf()
print(line(5)) # out the scope
上述代码说明函数是有其作用域的,内部函数的函数作用域在其定义的函数内部使用。
我们说python中一切数据皆对象(everything is object)。
那 可以将函数对象返回 即可使用相同的功能。
def line_conf():
def line(x):
return 2*x+1
return line
myline = line_conf()
print(myline(5))
如果内部函数使用了在其外部定义的变量呢?
def line_conf():
b = 15
def line(x):
return 2*x+b
return line # return a function object
b = 5
my_line = line_conf()
print(my_line(5))
可以看到在line中使用line函数外定义的b,可想而知line函数的运行结果不仅取决于函数的参数,还取决于内部函数使用的外部变量。 我们把b称为line的环境变量。
我们把这种使用了外部变量(环境变量)的内部函数称之为闭包。
实现原理
环境变量取值被保存在函数对象的closure属性中
print(my_line.__closure__)
print(my_line.__closure__[0].cell_contents)
closure里包含了一个元组(tuple)。这个元组中的每个元素是cell类型的对象。我们看到第一个cell包含的就是整数15,也就是我们创建闭包时的环境变量b的取值。
闭包的使用场景
#!/usr/bin/env python
#coding=utf-8
def generate_counter():
CNT = [0]
def add_one():
CNT[0] = CNT[0] + 1
return CNT[0]
return add_one
counter = generate_counter()
print counter() # 1
print counter() # 2
print counter() # 3
当闭包执行完后,仍然能够保持住当前的运行环境。
闭包可以根据外部作用域的局部变量来得到不同的结果
def make_filter(keep):
def the_filter(file_name):
file = open(file_name)
lines = file.readlines()
file.close()
filter_doc = [i for i in lines if keep in i]
return filter_doc
return the_filter
如果我们需要取得文件"result.txt"中含有"pass"关键字的行,则可以这样使用例子程序
filter = make_filter("pass")
filter_result = filter("result.txt")
关于闭包的两个注意事项
http://www.jb51.net/article/108916.htm
- 闭包中只能读、不能修改外部作用域局部变量的值。
>>> def foo():
m = 0
def f1():
m = 1
print (m)
print (m)
f1()
print (m)
>>> foo()
0 1 0
- 当循环结束以后,循环体中的临时变量i不会销毁,而是继续存在于执行环境中。还有一个python的现象是,python的函数只有在执行时,才会去找函数体里的变量的值。
flist = []
for i in range(3):
def foo(x): print x + i
flist.append(foo)
for f in flist:
f(2)
可能有些人认为这段代码的执行结果应该是2,3,4.但是实际的结果是4,4,4。这是因为当把函数加入flist列表里时,python还没有给i赋值,只有当执行时,再去找i的值是什么,这时在第一个for循环结束以后,i的值是2,所以以上代码的执行结果是4,4,4.