Python之初识闭包函数

Python中的闭包函数
通常定义函数是这样的:
deffoo(): pass
事实上,函数也可以嵌套在函数编程中,如下:
deffoo():
print("helloworldinfoo")
defbar():
print("helloworldinbar")当我们调用foo函数时,执行结果会是什么样子??
helloworldinfoo
如上所示,foo函数的第一层函数只会执行,bar函数不会执行。为什么?
事实上,无论函数写在哪一部分,它都只定义了一个函数,只有当函数被调用时,函数中的句子才会被执行。
在上面的例子中,虽然bar函数是在foo函数内部定义的,但它没有被执行,因此bar函数不会被执行。
这样,定义函数内部的函数没有效果吗??事实并非如此。
以下例子为例,将bar函数作为一个值返回到foo函数,以查看执行过程:
deffoo():
print("helloworldinfoo")
defbar():
print("helloworldinbar")
returnbar
f1=foo()
print(f1)此时,由于bar函数作为返回值返回到foo,因此foo函数的执行结果具有返回值。此时,定义一个变量f1来接收foo函数的执行返回结果,然后打印f1。
返回结果如下:
helloworldinfoo <functionfoo.<locals>.2941A60000000000>
可以看出,foo函数中定义的print语句首先打印出来,然后打印出foo函数中包含的bar函数的内存地址。既然是函数的内存地址,当然可以加括号来执行这个函数。
deffoo():
print("helloworldinfoo")
defbar():
print("helloworldinbar")
returnbar
f1=foo()
f1()此时,该代码的执行结果如下:
helloworldinfoo helloworldinbar
两个print语句都打印出来了。
在上面的例子中,首先定义函数foo,然后嵌套在foo函数中定义函数bar,然后返回函数bar的函数名,这是闭包函数的定义。事实上,闭包的定义是一个函数嵌套在一个函数中。
看下面的代码:
deffoo():
print("helloworldinfoo")
name="python"
defbar():
print(name)
print("helloworldinbar")
returnbar
f1=foo()
f1()在上面的例子中,将变量name定义为外层函数,然后将变量name打印在内层函数中。
此时,执行上述代码。打印name变量时,您将首先在bar函数中找到name变量,但bar函数中没有name变量。此时,根据python搜索变量的LEGB规则,您将继续在bar函数的外层搜索name变量。此时,您可以找到name变量,因此在这里打印的foo函数中定义的name值。
执行上述代码,打印结果如下:
helloworldinfoo python helloworldinbar
请记住,内层函数引用外层函数的局部变量是非常重要的。
在下面的例子中分析程序的执行过程:
首先运行foo函数,foo函数的执行结果是返回bar的函数名,此时将foo函数的执行结果定义为变量f1,
因此,此时f1等于bar函数的内存地址,然后f1加括号运行表示bar函数运行。
在执行bar函数的过程中,bar函数访问了外foo函数中定义的变量,这是一个典型的闭包函数。
使用封闭函数有什么好处??在上面的例子中,f1的值是bar函数的内存地址,f1加括号操作是bar函数的操作。
由于F1是一个全球变量,这意味着F1函数可以在整个程序的任何位置运行,然后定义一个函数,并在该函数内部调用F1函数,
deffoo():
print("helloworldinfoo")
name="python"
defbar():
print(name)
print("helloworldinbar")
returnbar
f1=foo()
deffunc():
name="aaaaa"
f1()
func()对程序执行过程进行分析:
1.运行func函数时,程序将首先在内存中申请一个空间来保存name变量值,然后运行f1函数。f1是全球定义的变量,因此您必须找到f1函数的内存地址。
2.f1加括号运行是在执行一个闭包函数时,在这个闭包函数中引用name变量。
3.name的变量已经在bar函数的外部定义,因此在func函数中调用f1函数,即bar函数时,引用的变量仍然是func函数内部定义的name变量,而不是func函数内部定义的name变量。
4.由于f1函数的内部已经包含了name函数的值,即使在func函数中也定义了name的变量,程序执行的结果仍然打印了fooo函数内部定义的name值。
相关建议:Python视频教程
程序执行结果:
helloworldinfoo python helloworldinbar
如何验证一个函数是闭包函数?首先,闭包函数有一个独特的属性:closure。
在上面的例子中,打印F1的__closure__属性。
deffoo():
name="python"
defbar():
print(name)
print("helloworldinbar")
returnbar
f1=foo()
print(f1.__closure__)打印结果如下:
(<cellat00000000000001DF57:Strobjectat000000001E79688>,)
能看见____closure_____________________________closure__返回元组元素的cell_contents方法打印name变量值。
deffoo():
name="python"
defbar():
print(name)
print("helloworldinbar")
returnbar
f1=foo()
print(f1.__closure__[0].cell_contents)打印结果如下:
python
可以看出,程序已经打印出name变量值,即然__closure__的返回结果是一个元组,所以这个元组必须包含多个值,看下面的例子。
在foo函数中定义多个变量,然后在bar函数中打印几个变量值,然后运行此闭包函数,打印闭包函数__closure__方法。
deffoo():
print("helloworldinfoo")
name1="python1"
name2="python2"
name3="python3"
name4="python4"
defbar():
print(name1)
print(name2)
print(name3)
print(name4)
print("helloworldinbar")
returnbar
f1=foo()
print(f1.__closure__)程序执行结果:
(<2145708,cellat000000000:C926000000,strobjectat00000021C92000>, <0000000,00000,00000,45A08:Bjectat000000000000021C93>, <21457688,cellat000000000000:Strobjectat00000000295BE3000000>, <2145C185C18,cellat000000000:strobjectat000000000000000002968>)
由于在foo函数中定义了四个变量,并且在bar函数中引用了这四个变量,因此打印这个封闭函数的__closure__方法,返回元组中有4个元素。
这四个字符串对象的值现在可以单独打印回元组。
deffoo():
name1="python1"
name2="python2"
name3="python3"
name4="python4"
defbar():
print(name1)
print(name2)
print(name3)
print(name4)
print("helloworldinbar")
returnbar
f1=foo()
print(f1.__closure__[0].cell_contents)
print(f1.__closure__[1].cell_contents)
print(f1.__closure__[2].cell_contents)
print(f1.__closure__[3].cell_contents)程序执行结果:
python1 python2 python3 python4
所以还有最后一个问题,那就是闭包函数的内层函数必须返回吗??
以下示例:
deffoo(): name="python1" defbar(): print(name) print(bar.__closure__) foo()
定义了嵌套函数,然后嵌套函数的内层函数没有返回,而是直接打印了内层函数__closure方法,然后直接调用外部函数。
程序执行结果:
(<000000,cellat000002157080:D9688,strobjectat000000000021D9688>,)
内层函数引用的变量对象仍然打印出来,说明闭包函数的内层函数必须返回。
闭包函数的内层函数能调用全局变量吗??
将外层函数内部定义的变量改为全局变量,然后在内层函数中引用。
name="python1" deffoo(): defbar(): print(name) print(bar.__closure__) f=foo() print(f)
程序执行结果:
None None
可以看出,程序的执行结果是两个None,嵌套函数的内层函数__closure__函数值为none,说明foo函数内嵌套函数bar调用的全局变量不成功,所以上面的例子不是封闭函数。
关于闭包函数的一些总结:
闭包的定义如下:
(1)函数内部定义的函数称为内部函数。
(2)内部函数调用外部函数的局部变量。
(3)即使内部函数返回,局部变量也可以使用。
(4)闭包函数的内层函数通常会返回到外部函数。
(5)闭包函数的外部函数可以在任何地方调用,而不再受函数定义时级别的限制。
闭包函数的作用
1.闭包函数自带函数作用域
在函数执行过程中,正常意义上的函数搜索变量的顺序是逐层向外搜索,符合LEGB(Local->Enclose->Global->Built in)法则的,
然而,对于闭包函数,搜索变量只会找到内部函数外的一层,因为闭包函数本身有一层功能域,因此符合要求"闭包"两个字的意思。
2.延迟计算(又称惰性计算)
看下面的例子
deffunc(): name="python" defbar(): print(name) returnbar f=func() print(f.__closure)
在上面的例子中,foo()函数的返回结果是一个包含自己状态的函数,但实际上这个函数并没有执行,
以后想要执行这个自带状态的函数时,可以执行func()返回结果赋值的变量加括号。
3.闭包可以用来保持一个函数的一种状态。
例子:
name="python"
deffunc():
print("Ilike%s"%name)
func()上述代码执行结果将打印一行:"I like python"
但我们知道,如果在不同的地方调用func函数,打印结果可能会有所不同,所以如果我想在任何地方调用func函数,打印结果都是"I like python"当时,可以使用闭包。
defunc1():
name="python"
deffunc():
print("Ilike%s"%name)
returnfunc
func=func1()
func()如上图所示,一层函数func1包含在func函数外,执行func1函数,然后将func1函数的返回结果赋值给func这个变量。
此时func是一个闭包函数,func函数加括号即可执行。
此外,我们必须知道func函数的执行结果将在此时打印"I like python"无论func函数在程序的哪个位置被调用,执行结果都是一样的。
