通俗易懂解释python闭包,闭包python 菜鸟教程_2

  通俗易懂解释python闭包,闭包python 菜鸟教程

  

  python中的闭包函数

  通常我们这样定义函数:

  deffoo():

  事实上,在函数式编程中,函数也可以嵌套在函数内部,传递如下:

  deffoo():

  print( hello world info )

  defbar():

  Print(helloworldinbar )此时,我们调用foo函数。执行结果会是什么样子?

  helloworldinfoo的结果如上图所示。只会执行第一层的foo函数,不会执行bar函数。为什么?

  其实不管函数的哪一部分写进去,都只是定义了一个函数。只有当这个函数被调用时,函数内部的语句才会被执行。

  在上面的例子中,虽然bar函数是在foo函数内部定义的,但是并没有执行,所以bar函数也不会执行。

  所以,函数内部定义的函数是没有用的?其实不是这样的。

  看下面这个例子。将bar函数作为一个值返回给foo函数,以查看执行过程:

  deffoo():

  print( hello world info )

  defbar():

  打印( helloworldinbar )

  返回栏

  f1=foo()

  此时Print(f1)因为bar函数作为返回值返回给foo,所以foo函数执行的结果是有返回值的。此时定义一个变量f1接收foo函数执行的返回结果,然后打印f1。

  返回的结果如下:

  helloworldinfoo

  function foo . locales . barat0x 00000002941 a60可以看到,首先打印的是foo函数中定义的一条print语句,然后打印的是foo函数中包含的bar函数的内存地址。既然是函数的内存地址,当然可以加括号来执行这个函数。

  deffoo():

  print( hello world info )

  defbar():

  打印( helloworldinbar )

  返回栏

  f1=foo()

  F1()此时,这段代码的执行结果是:

  helloworldinfoo

  helloworldinbar的两个打印语句被打印出来。

  在上面的例子中,首先定义了一个函数foo,然后在foo函数内部嵌套了一个函数bar,然后返回函数bar的函数名。这就是闭包函数的定义。实际上,闭包的定义是一个函数嵌套在另一个函数内部。

  看一下下面的代码:

  hp;toolbar:false">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函数时,其引用的变量依然是foo函数内部定义的name变量,而不是func函数内部定义的name变量。

  4.因为f1函数的内部已经包含了name这个函数的值,所以就算在func函数内部也定义了name这个变量,程序执行的结果打印的依然是foo函数内部定义的name的值。

  相关推荐:《Python视频教程》

  程序执行结果:

  

helloworldinfoo

  python

  helloworldinbar

怎样验证一个函数是闭包函数?首先,闭包函数都有一个特有的属性:closure。

  在上面的例子里,打印f1的__closure__属性。

  

deffoo():

  name="python"

  

  defbar():

  print(name)

  print("helloworldinbar")

  returnbar

  

  f1=foo()

  print(f1.__closure__)

打印结果如下:

  

(<cellat0x0000000001DF5708:strobjectat0x0000000001E79688>,)
可以看到__closure__属性的打印结果是一个元组形式的,其值就是f1函数的外层函数作用域,此时可以调用__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__)

程序执行结果:

  

(<cellat0x0000000002145708:strobjectat0x00000000021C9260>,

  <cellat0x0000000002145A08:strobjectat0x00000000021C93B0>,

  <cellat0x0000000002145768:strobjectat0x000000000295BE30>,

  <cellat0x0000000002145C18:strobjectat0x0000000002963880>)

由于在foo函数内部定义了4个变量,而且在bar函数内部引用了这4个变量,所以打印这个闭包函数的__closure__方法,返回的元组中就有4个元素。

  现在可以分别打印返回的元组中的这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__方法,然后直接调用外层函数。

  程序执行结果:

  

(<cellat0x0000000002155708:strobjectat0x00000000021D9688>,)
依然打印出了内层函数的引用的变量对象,这说明闭包函数的内层函数还一定要返回。

  闭包函数的内层函数可以调用全局变量吗??

  把外层函数内部定义的变量改为全局变量,然后在内层函数中引用这个变量。

  

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"时,就可以使用闭包了。

  

deffunc1():

  

  name="python"

  deffunc():

  print("Ilike%s"%name)

  returnfunc

  

  func=func1()

  func()

如上图所示,在func函数外面再包含一层函数func1,执行func1函数,再把func1函数的返回结果赋值给func这个变量。

  此时func就是一个闭包函数了,把func函数加括号就可以执行了。

  而且我们一定知道,此时func函数的执行结果一定会打印"I like python"这句话,而且不管func函数在程序的哪个位置被调用,执行结果都是一样的。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: