欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

python中的閉包函數(shù)

 更新時(shí)間:2018年02月09日 08:40:24   作者:renpingsheng  
這篇文章主要介紹了python中的閉包函數(shù),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下

閉包函數(shù)初探

通常我們定義函數(shù)都是這樣定義的

def foo():
 pass

其實(shí)在函數(shù)式編程中,函數(shù)里面還可以嵌套函數(shù),如下面這樣

def foo():
 print("hello world in foo")
 
 def bar():
 print("hello world in bar")

此時(shí)我們調(diào)用foo函數(shù),執(zhí)行結(jié)果會(huì)是什么樣子的呢??

hello world in foo

結(jié)果如上所示,只會(huì)執(zhí)行foo函數(shù)的第一層函數(shù),bar函數(shù)是不會(huì)被執(zhí)行的。為什么呢

實(shí)際上來(lái)說(shuō),不管函數(shù)寫(xiě)在哪個(gè)部分,那都只是定義了一個(gè)函數(shù),只有這個(gè)函數(shù)被調(diào)用,函數(shù)內(nèi)部的語(yǔ)句才會(huì)被執(zhí)行

在上面的例子中,bar函數(shù)雖然在foo函數(shù)內(nèi)部定義了,但是并沒(méi)有被執(zhí)行,所以bar函數(shù)是不會(huì)被執(zhí)行的這樣說(shuō)來(lái),定義在一個(gè)函數(shù)內(nèi)部的函數(shù)就沒(méi)什么作用了嗎??其實(shí)不是這樣的。

來(lái)看下面的例子,把bar函數(shù)作為一個(gè)值返回給foo函數(shù),來(lái)看執(zhí)行過(guò)程

def foo():
 print("hello world in foo")
 
 def bar():
 print("hello world in bar")
 return bar
f1=foo()
print(f1)

此時(shí),由于bar函數(shù)作為一個(gè)返回值被返回給了foo,所以foo函數(shù)執(zhí)行結(jié)果是有返回值的

此時(shí)定義一個(gè)變量f1來(lái)接收f(shuō)oo函數(shù)的執(zhí)行返回結(jié)果,然后打印f1

返回的結(jié)果如下

hello world in foo
<function foo.<locals>.bar at 0x0000000002941A60>

可以看到首先打印了foo函數(shù)中定義的一個(gè)print語(yǔ)句,接著打印的是foo函數(shù)中包含的bar函數(shù)的內(nèi)存地址

既然是一個(gè)函數(shù)的內(nèi)存地址,當(dāng)然可以加括號(hào)來(lái)執(zhí)行這個(gè)函數(shù)

def foo():
 print("hello world in foo")
 def bar():
 print("hello world in bar")
 return bar
f1=foo()
f1()

此時(shí),這段代碼的執(zhí)行結(jié)果為:

hello world in foo
hello world in bar

兩個(gè)print語(yǔ)句都被打印出來(lái)了。

在上面的例子里,首先定義了一個(gè)函數(shù)foo,接著在foo函數(shù)內(nèi)部又嵌套定義了一個(gè)函數(shù)bar,然后返回函數(shù)bar的函數(shù)名,這就是閉包函數(shù)的定義方式。

其實(shí),閉包的定義就是一個(gè)函數(shù)內(nèi)部又嵌套了一個(gè)函數(shù)

來(lái)看下面的這段代碼

 def foo():
 print("hello world in foo")
 name="python"
 def bar():
  print(name)
  print("hello world in bar")
 return bar
 
 f1=foo()
 f1()

在上面的例子里,在外層函數(shù)中定義了一個(gè)變量name,然后在內(nèi)層函數(shù)中打印這個(gè)變量name

此時(shí)執(zhí)行上面的代碼,在打印name這個(gè)變量的時(shí)候,會(huì)先在bar函數(shù)內(nèi)部查找name這個(gè)變量,但是bar函數(shù)里面是沒(méi)有name這個(gè)變量的,

此時(shí)根據(jù)python查找變量的LEGB法則,會(huì)到bar函數(shù)的外面一層去繼續(xù)查找name這個(gè)變量,此時(shí)可以找到name這個(gè)變量

所以這里打印的foo函數(shù)中定義的name的值

執(zhí)行上面的代碼,打印結(jié)果如下

hello world in foo
python
hello world in bar

這里要記住很重要的一點(diǎn)就是:

內(nèi)層函數(shù)引用了外層函數(shù)的局部變量

來(lái)分析下上面的例子中程序的執(zhí)行過(guò)程:

首先運(yùn)行foo函數(shù),foo函數(shù)的執(zhí)行結(jié)果是返回bar的函數(shù)名,此時(shí)又把foo函數(shù)的執(zhí)行結(jié)果定義給了變量f1,
所以此時(shí)f1就等于bar這個(gè)函數(shù)的內(nèi)存地址,然后f1加括號(hào)運(yùn)行就表示運(yùn)行了bar函數(shù)。
在執(zhí)行bar函數(shù)的過(guò)程中,bar函數(shù)訪問(wèn)到了外層foo函數(shù)中定義的變量,這就是一個(gè)典型的閉包函數(shù)
那使用閉包函數(shù)有什么好處呢??在上面的例子里,f1的值是bar函數(shù)的內(nèi)存地址,f1加括號(hào)運(yùn)行就是在運(yùn)行bar函數(shù)。

又由于f1是一個(gè)全局變量,這意味著可以在整個(gè)程序的任意位置都可以運(yùn)行f1函數(shù),此時(shí)再定義一個(gè)函數(shù),在這個(gè)函數(shù)內(nèi)部調(diào)用f1函數(shù),

 def foo():
 print("hello world in foo")
 name = "python"
 
 def bar():
  print(name)
  print("hello world in bar")
 return bar
 
 f1 = foo()
 
 def func():
 name = "aaaaa"
 f1()
 func()

來(lái)分析一下程序的執(zhí)行過(guò)程:

1.運(yùn)行func函數(shù),程序會(huì)先在內(nèi)存中申請(qǐng)一塊空間以保存name變量的值,然后運(yùn)行f1函數(shù),f1是在全局中定義的變量,所以一定可以找到f1函數(shù)的內(nèi)存地址

2.f1加括號(hào)運(yùn)行,就是在執(zhí)行一個(gè)閉包函數(shù),這個(gè)閉包函數(shù)內(nèi)部引用了name這個(gè)變量

3.name這個(gè)變量在bar函數(shù)的外部已經(jīng)定義了,所以在func函數(shù)內(nèi)部調(diào)用f1函數(shù),也就是bar函數(shù)時(shí),其引用的變量依然是foo函數(shù)內(nèi)部定義的name變量,而不是func函數(shù)內(nèi)部定義的name變量,

4.因?yàn)閒1函數(shù)的內(nèi)部已經(jīng)包含了name這個(gè)函數(shù)的值,所以就算在func函數(shù)內(nèi)部也定義了name這個(gè)變量,程序執(zhí)行的結(jié)果打印的依然是foo函數(shù)內(nèi)部定義的name的值

程序執(zhí)行結(jié)果

hello world in foo
python
hello world in bar

怎樣驗(yàn)證一個(gè)函數(shù)是閉包函數(shù)

首先,閉包函數(shù)都有一個(gè)特有的屬性:closure

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

 def foo():
 name = "python"
 def bar():
  print(name)
  print("hello world in bar")
 return bar
 f1 = foo()
 print(f1.__closure__)

打印結(jié)果如下:

(<cell at 0x0000000001DF5708: str object at 0x0000000001E79688>,)

可以看到__closure__屬性的打印結(jié)果是一個(gè)元組形式的,其值就是f1函數(shù)的外層函數(shù)作用域

此時(shí)可以調(diào)用__closure__返回的元組的元素的cell_contents方法打印出name變量的值

 def foo():
 name = "python"
 
 def bar():
  print(name)
  print("hello world in bar")
 return bar
 
 f1 = foo()
 print(f1.__closure__[0].cell_contents)

打印結(jié)果如下:

python

可以看到程序已經(jīng)打印出name變量的值了

即然__closure__的返回結(jié)果是一個(gè)元組,那么這個(gè)元組中一定是可以包含多個(gè)值的,看下面的例子

在foo函數(shù)內(nèi)部定義多個(gè)變量,然后在bar函數(shù)內(nèi)部打印幾個(gè)變量的值,

然后運(yùn)行這個(gè)閉包函數(shù),打印閉包函數(shù)的__closure__方法   

 def foo():
 print("hello world in foo")
 name1 = "python1"
 name2 = "python2"
 name3 = "python3"
 name4 = "python4" 
 def bar():
  print(name1)
  print(name2)
  print(name3)
  print(name4)
  print("hello world in bar")
 return bar 
 f1 = foo()
 print(f1.__closure__)

程序執(zhí)行結(jié)果

(<cell at 0x0000000002145708: str object at 0x00000000021C9260>, 
<cell at 0x0000000002145A08: str object at 0x00000000021C93B0>, 
<cell at 0x0000000002145768: str object at 0x000000000295BE30>, 
<cell at 0x0000000002145C18: str object at 0x0000000002963880>)

由于在foo函數(shù)內(nèi)部定義了4個(gè)變量,而且在bar函數(shù)內(nèi)部引用了這4個(gè)變量,所以打印這個(gè)閉包函數(shù)的__closure__方法,返回的元組中就有4個(gè)元素

現(xiàn)在可以分別打印返回的元組中的這4個(gè)字符串對(duì)象的值了   

 def foo():
 name1 = "python1"
 name2 = "python2"
 name3 = "python3"
 name4 = "python4"
 
 def bar():
  print(name1)
  print(name2)
  print(name3)
  print(name4)
  print("hello world in bar")
 return bar 
 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)

程序執(zhí)行結(jié)果

python1
python2
python3
python4

那么現(xiàn)在還剩下最后一個(gè)問(wèn)題了,那就是閉包函數(shù)的內(nèi)層函數(shù)一定要返回嗎??

來(lái)看下面一個(gè)例子

 def foo():
 name = "python1" 
 def bar():
  print(name)
 print(bar.__closure__) 
 foo()

定義了一個(gè)嵌套函數(shù),然后這個(gè)嵌套函數(shù)的內(nèi)層函數(shù)沒(méi)有被返回,而是直接打印內(nèi)層函數(shù)的__closure__方法,然后直接調(diào)用外層函數(shù)。

程序執(zhí)行結(jié)果

(<cell at 0x0000000002155708: str object at 0x00000000021D9688>,)

依然打印出了內(nèi)層函數(shù)的引用的變量對(duì)象

這說(shuō)明閉包函數(shù)的內(nèi)層函數(shù)還一定要返回

閉包函數(shù)的內(nèi)層函數(shù)可以調(diào)用全局變量嗎??

把外層函數(shù)內(nèi)部定義的變量改為全局變量,然后在內(nèi)層函數(shù)中引用這個(gè)變量

 name = "python1"
 def foo():
 def bar():
  print(name) 
 print(bar.__closure__)
 f=foo()
 print(f)

程序執(zhí)行結(jié)果

None
None

可以看到,程序的執(zhí)行結(jié)果是兩個(gè)None,嵌套函數(shù)的內(nèi)層函數(shù)的__closure__函數(shù)的值為None

這說(shuō)明foo函數(shù)的內(nèi)層嵌套函數(shù)bar調(diào)用的全局變量沒(méi)有成功,所以上面的例子不是一個(gè)閉包函數(shù)

關(guān)于閉包函數(shù)的一些總結(jié):

閉包的定義為:

    在函數(shù)內(nèi)部定義的函數(shù),稱為內(nèi)部函數(shù)
    內(nèi)部函數(shù)調(diào)用了外部函數(shù)的局部變量
    即使內(nèi)部函數(shù)返回了,還是可以使用局部變量
    通常閉包函數(shù)的內(nèi)層函數(shù)都要被返回給外部函數(shù)
    閉包函數(shù)的外部函數(shù)可以在任何地方被調(diào)用,而不再受函數(shù)定義時(shí)層級(jí)的限制

閉包函數(shù)的作用

1.閉包函數(shù)自帶函數(shù)作用域

正常意義上的函數(shù),在函數(shù)執(zhí)行過(guò)程中查找變量的順序是一層一層向外找,符合LEGB(Local->Enclose->Global->Built in)法則的,

但是對(duì)閉包函數(shù)來(lái)說(shuō),查找變量只會(huì)找內(nèi)部函數(shù)外面的那一層,因?yàn)殚]包函數(shù)本身就自帶一層作用域,這樣才符合"閉包"兩個(gè)字的意思

2.延遲計(jì)算(也叫惰性計(jì)算)

看下面的例子

 def func():
 name="python"
 def bar():
  print(name)
 return bar 
 f=func()
 print(f.__closure)

在上面的例子里,執(zhí)行foo()函數(shù)的返回結(jié)果是一個(gè)包含自帶的某種狀態(tài)的函數(shù),實(shí)際上這個(gè)函數(shù)并沒(méi)有執(zhí)行,

以后想執(zhí)行這個(gè)自帶狀態(tài)的函數(shù)時(shí),把func()返回結(jié)果所賦值的那個(gè)變量加括號(hào)就可以執(zhí)行了,

3.要想讓一個(gè)函數(shù)始終保持一種狀態(tài),就可以使用閉包

例子:

 name="python" 
 def func():
 print("I like %s" % name) 
 func()

上面的代碼執(zhí)行結(jié)果會(huì)打印一行:"I like python"

但是我們知道,在不同的地方調(diào)用func函數(shù),打印的結(jié)果很大可能是不一樣的

那么如果我想不管在什么地方調(diào)用func函數(shù),打印的結(jié)果都是"I like python"時(shí),

就可以使用閉包了。

 def func1(): 
 name="python"
 def func():
  print("I like %s" % name)
 return func
 func=func1()
 func()

如上圖所示,在func函數(shù)外面再包含一層函數(shù)func1,執(zhí)行func1函數(shù),再把func1函數(shù)的返回結(jié)果賦值給func這個(gè)變量

此時(shí)func就是一個(gè)閉包函數(shù)了,把func函數(shù)加括號(hào)就可以執(zhí)行了

而且我們一定知道,此時(shí)func函數(shù)的執(zhí)行結(jié)果一定會(huì)打印"I like python"這句話,而且不管func函數(shù)在程序的哪個(gè)位置被調(diào)用,執(zhí)行結(jié)果都是一樣的

相關(guān)文章

  • python版單鏈表反轉(zhuǎn)

    python版單鏈表反轉(zhuǎn)

    這篇文章主要為大家詳細(xì)介紹了python版單鏈表反轉(zhuǎn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • python實(shí)現(xiàn)時(shí)間序列自相關(guān)圖(acf)、偏自相關(guān)圖(pacf)教程

    python實(shí)現(xiàn)時(shí)間序列自相關(guān)圖(acf)、偏自相關(guān)圖(pacf)教程

    這篇文章主要介紹了python實(shí)現(xiàn)時(shí)間序列自相關(guān)圖(acf)、偏自相關(guān)圖(pacf)教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-06-06
  • python數(shù)字圖像處理實(shí)現(xiàn)直方圖與均衡化

    python數(shù)字圖像處理實(shí)現(xiàn)直方圖與均衡化

    在圖像處理中,直方圖是非常重要,也是非常有用的一個(gè)處理要素。這篇文章主要介紹了python數(shù)字圖像處理實(shí)現(xiàn)直方圖與均衡化,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • python寫(xiě)入中英文字符串到文件的方法

    python寫(xiě)入中英文字符串到文件的方法

    這篇文章主要介紹了python寫(xiě)入中英文字符串到文件的方法,實(shí)例分析了Python操作中英文字符串的技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下
    2015-05-05
  • python動(dòng)態(tài)網(wǎng)頁(yè)批量爬取

    python動(dòng)態(tài)網(wǎng)頁(yè)批量爬取

    這篇文章主要介紹了python動(dòng)態(tài)網(wǎng)頁(yè)批量爬取的方法,主要針對(duì)四六級(jí)成績(jī)批量爬取,感興趣的小伙伴們可以參考一下
    2016-02-02
  • python局部賦值的規(guī)則

    python局部賦值的規(guī)則

    Python提出如下假設(shè):如果在函數(shù)體內(nèi)的任何地方對(duì)變量賦值,則Python將名稱添加到局部命名空間中。
    2013-03-03
  • Flask模擬實(shí)現(xiàn)CSRF攻擊的方法

    Flask模擬實(shí)現(xiàn)CSRF攻擊的方法

    這篇文章主要介紹了Flask模擬實(shí)現(xiàn)CSRF攻擊的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • AI生成圖片Stable?Diffusion環(huán)境搭建與運(yùn)行方法

    AI生成圖片Stable?Diffusion環(huán)境搭建與運(yùn)行方法

    Stable?Diffusion是一種基于擴(kuò)散過(guò)程的生成模型,由Ge?et?al.在2021年提出,該模型利用了隨機(jī)變量的穩(wěn)定分布,通過(guò)遞歸地應(yīng)用擴(kuò)散過(guò)程來(lái)生成高質(zhì)量的圖像,這篇文章主要介紹了AI圖片生成Stable?Diffusion環(huán)境搭建與運(yùn)行,需要的朋友可以參考下
    2023-05-05
  • Python如何發(fā)送與接收大型數(shù)組

    Python如何發(fā)送與接收大型數(shù)組

    這篇文章主要介紹了Python如何發(fā)送與接收大型數(shù)組,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-08-08
  • 不知道這5種下劃線的含義,你就不算真的會(huì)Python!

    不知道這5種下劃線的含義,你就不算真的會(huì)Python!

    Python是一種高級(jí)程序語(yǔ)言,其核心設(shè)計(jì)哲學(xué)是代碼可讀性和語(yǔ)法,能夠讓程序員用很少的代碼來(lái)表達(dá)自己的想法。這篇文章主要介紹了不知道這5種下劃線的含義,你就不算真的會(huì)Python!對(duì)此標(biāo)題感興趣的朋友一起閱讀本文吧
    2018-10-10

最新評(píng)論