Python中闭包的理解

来源:互联网 发布:大数据营销案例沃尔玛 编辑:程序博客网 时间:2024/06/05 12:04

Python中闭包的理解:

Num01–>定义:

官方定义: 闭包是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数,这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

自已的理解为: 如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。

举一个简单的例子来说明:

def addx(x):    def addy(y):        return x + y    return addyxy = addx(6)print(type(xy))print(xy.__name__)print(xy(66))# 结果是:# <class 'function'># addy# 72

对以上代码加以说明:
如果在一个内部函数里:addy(y)就是这个内部函数。
对在外部作用域(但不是全局作用域)的变量进行引用:x就是被引用的变量,x在外部作用域addx(x)函数里,但不在全局作用域里。
那么这个内部函数addy(y)就是闭包。

闭包的_closure_属性

一个函数和它的环境变量合在一起,就构成了一个闭包(closure)。在Python中,所谓的闭包是一个包含有环境变量取值的函数对象。环境变量取值被保存在函数对象的_closure_属性中。
如下案例加以说明:

b = 5def line_func():    b = 55    def line(x):        return 2 * x + b    return linemy_line = line_func()print(my_line.__closure__)print(my_line.__closure__[0].cell_contents)# 结果如下:# (<cell at 0x0000028F6E228C18: int object at 0x00000000508A0890>,)# 55

对以上代码加以说明:
_closure_里面包含了一个元组。元组中每一个对象类型都是cell类型的。元组中的第0个元素就是55,也就是创建闭包时的环境变量b的取值。

Num02–>为什么要使用闭包

    不同的是闭包本身就是个方法。和类一样,我们在编程时经常会把通用的东西抽象成类,以复用通用的功能。闭包也是一样,当我们需要函数粒度的抽象时,闭包就是一个很好的选择。    在这点上闭包可以被理解为一个只读的对象,你可以给他传递一个属性,但它只能提供给你一个执行的接口。因此在程序中我们经常需要这样的一个函数对象——闭包,来帮我们完成一个通用的功能。

Num03–>使用闭包注意事项

Test01–>闭包中是不能修改外部作用域局部变量的值

看如下案例:

def func():    m = 0    def func_in():        m = 1        print("++%d" % m)    print("--%d" % m)    func_in()    print("==%d" % m)print("最后打印:%s" % func())# 结果如下:# --0# ++1# ==0# 最后打印:None

对以上代码加以说明:
从执行结果可以看出,虽然在闭包里面也定义了一个变量m,但是其不会改变外部函数中的局部变量m的值。

Test02–>局部变量的问题

def foo():    a = 1    def bar():        a = a + 1        return a    return barf=foo()print(f())# 结果如下:# Traceback (most recent call last):#   File "E:/pycharmProject/Test24.py", line 82, in <module>#     print(f())#   File "E:/pycharmProject/Test24.py", line 77, in bar#     a = a + 1# UnboundLocalError: local variable 'a' referenced before assignment

以上代码加以说明:
这是因为在执行代码 f = foo()时,Python会导入全部的闭包函数体bar()来分析其的局部变量。Python规则指定所有在赋值语句左面的变量都是局部变量,则在闭包bar()中,变量a在赋值符号”=”的左面,被Python认为是bar()中的局部变量。再接下来执行print(f())时,程序运行至a = a + 1时,因为先前已经把a归为bar()中的局部变量,所以python会在bar()中去找在赋值语句右面的a的值,结果找不到,就会报错。

两种解决办法

#第一种解决办法:自由变量为不可变对象def foo():    a = 1    def bar():        nonlocal a        a = a + 1        return a    return barf=foo()print(f())#第二种解决办法:不是太好,不建议,自由变量为可变对象def foo():    a = [1]    def bar():        a[0] = a[0] + 1        return a[0]    return barf=foo()print(f())

Test03–>Python函数式编程中一个问题

看如下代码:

for a in range(10):     print(i)

在程序里面经常会出现这类的循环语句,Python的问题就在于:当循环结束以后,循环体中的临时变量a不会销毁,而是继续存在于执行环境中

还有一个Python的现象是:Python的函数只有在执行时,才会去找函数体里的变量的值。

listname = []for a in range(3):    print("a==%s" % a)    def foo(x):        print("a--%s" % a)        print(x + a)        print("a++%s" % a)    listname .append(foo)for f in listname :    f(2)# 看结果是:# a==0# a==1# a==2# a--2# 4# a++2# a--2# 4# a++2# a--2# 4# a++2

可能有些人认为这段代码的执行结果应该是2,3,4.但是实际的结果是4,4,4。这是因为当把函数加入flist列表里时,Python还没有给a赋值,只有当执行时,再去找a的值是什么。这时在for循环语句中,已经将a的值赋值为2,所以以上代码的执行结果是4,4,4。

如果要想结果是2,3,4,看如下代码修改:

listname = []for a in range(3):    print("a==%s" % a)    def foo(x, y=a):        print("a==%s" % a)        print("y--%s" % y)        print(x + y)        print("y++%s" % y)        print("a==%s" % a)    listname.append(foo)for f in listname:    f(2)# 看结果是:# a==0# a==1# a==2# a==2# y--0# 2# y++0# a==2# a==2# y--1# 3# y++1# a==2# a==2# y--2# 4# y++2# a==2

相信读者看到这里就能明白函数式编程的特点了。

Num04–>闭包的作用

作用1:当闭包执行完后,仍然能够保持住当前的运行环境。
作用2:闭包可以根据外部作用域的局部变量来得到不同的结果。
作用3:闭包对数据的持久化以及按配置产生不同的功能,是很有帮助的

0 0