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
  1. 当闭包执行完后,仍然能够保持住当前的运行环境。

  2. 闭包可以根据外部作用域的局部变量来得到不同的结果

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

  1. 闭包中只能读、不能修改外部作用域局部变量的值。
>>> 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.

results matching ""

    No results matching ""