您的位置:首页 > 编程语言 > Python开发

Python中闭包的理解

2017-04-24 17:04 302 查看

Python中闭包的理解:

Num01–>定义:

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

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

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

def addx(x):
def addy(y):
return x + y
return addy
xy = 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 = 5

def line_func():
b = 55

def line(x):
return 2 * x + b

return line

my_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 bar
f=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 bar
f=foo()
print(f())

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

return bar
f=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:闭包对数据的持久化以及按配置产生不同的功能,是很有帮助的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息