>>a=1>>>deffoo():b=2c=3print"locals:%s"%locals()return"result:%d"%(a+b+c)>>>a=1>>>deffoo():b=2c=3print"locals:%" />

亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

淺談Python中的作用域規(guī)則和閉包

系統(tǒng) 1841 0

在對Python中的閉包進行簡單分析之前,我們先了解一下Python中的作用域規(guī)則。關于Python中作用域的詳細知識,有很多的博文都進行了介紹。這里我們先從一個簡單的例子入手。

Python中的作用域

假設在交互式命令行中定義如下的函數(shù):

            
>>> a = 1
>>> def foo():
    b = 2
    c = 3
    print "locals: %s" % locals()
    return "result: %d" % (a + b +c)
>>> a = 1
>>> def foo():
    b = 2
    c = 3
    print "locals: %s" % locals()
    return "result: %d" % (a + b +c)

          

上述代碼先給a賦值1,緊接著定義了一個函數(shù):foo()。在函數(shù)foo()中我們定義了兩個整數(shù)b和c,函數(shù)的返回值為a、b、c三個數(shù)的和。

對上述函數(shù)進行驗證:

            
# result
>>> foo()
locals: {'c': 3, 'b': 2}
result: 6
# result
>>> foo()
locals: {'c': 3, 'b': 2}
result: 6

          

根據(jù)驗證的結果,foo()函數(shù)的返回值為6。上述的函數(shù)定義中只有b和c兩個變量的賦值,那調用函數(shù)是如何判斷a的值呢?這涉及到函數(shù)的作用域規(guī)則。本文摘錄《Python參考手冊(第4版)》中的相關論述:

每次執(zhí)行一個函數(shù)時, 就會創(chuàng)建心得局部命名空間。該命名空間代表一個局部環(huán)境,其中包含函數(shù)參數(shù)的名稱和在函數(shù)體內賦值的變量名稱。解析這些名稱時:

解釋器將首先搜索局部命名空間;

如果沒有找到匹配的名稱,它就會搜索全局命名空間(函數(shù)的全局命名空間始終是定義該函數(shù)的模塊);

如果解釋器在全局命名空間中也找不到匹配值,最終會檢查內置命名空間;

如果在內置命名空間中也找不到匹配值,就會引發(fā)NameError異常。

對應于上面的例子,foo函數(shù)首先會在局部命名空間中找三個變量的匹配值。上述代碼中的locals()方法給出了foo函數(shù)局部命名空間的內容。可以看出,局部命名空間是一個字典,包含b和c的值,這是因為我們在foo函數(shù)中定義了這兩個變量。然而,局部命名空間中不包含a的值,所以就需要在全局命名空間中尋找。可以使用__globals__獲取一個函數(shù)的局部命名空間。

            
# foo函數(shù)的全局命名空間
>>> foo.__globals__
{'a': 1, '__builtins__': 
            
              , '__package__': None, '__name__': '__main__', 'foo': 
              
                , '__doc__': None}
# foo函數(shù)的全局命名空間
>>> foo.__globals__
{'a': 1, '__builtins__': 
                
                  , '__package__': None, '__name__': '__main__', 'foo': 
                  
                    , '__doc__': None}

                  
                
              
            
          

foo函數(shù)的全局命名空間中包含了內置函數(shù)模塊、foo函數(shù)、變量a以及其他的一些參數(shù)。由于在foo函數(shù)的全局命名空間中找到了變量a,foo函數(shù)便返回三個變量的和。

Python閉包

上述的Python作用域規(guī)則具有普遍性。然而,在Python中“一切皆對象”,函數(shù)也不例外。這也就是說可以把函數(shù)當作參數(shù)傳遞給其他的函數(shù),也可以放在數(shù)據(jù)結構中,還可以作為函數(shù)的返回結果。在這種情況下,Python的作用域規(guī)則會發(fā)生什么變化呢?我們還是舉一個例子:

            
>>> def foo():
    a = 1
    def bar():
      b = 2
      c = 3
      return a + b + c
    return bar

>>> def foo():
    a = 1
    def bar():
      b = 2
      c = 3
      return a + b + c
    return bar

          

在這個例子中,我們定義了一個函數(shù)foo,并對變量a賦值。不過與之前的例子不同的是,在函數(shù)foo中我們還嵌套了一個函數(shù)bar,并且還定義了兩個變量,這個函數(shù)是作為函數(shù)foo的返回值。根據(jù)上面的作用域規(guī)則,函數(shù)foo的局部作用域既不是函數(shù)bar的局部作用域,也不是它的全局作用域,那函數(shù)bar能否正確匹配變量a的值呢?我們我們來驗證一下這個函數(shù)是否能夠正常運行。

            
# 調用函數(shù)foo()
>>> bar = foo()
# 返回值bar是一個函數(shù)
>>> bar

            
              
# 調用bar()
>>> bar()
# 結果顯示為三個變量之和
6
            
          

以上的驗證結果說明,在上述嵌套的函數(shù)中,內部函數(shù)可以正確地引用外部函數(shù)的變量,即使外部的函數(shù)已經(jīng)返回。

這種內部函數(shù)的局部作用域中可以訪問外部函數(shù)局部作用域中變量的行為,我們稱為: 閉包。內部函數(shù)可以訪問外部函數(shù)變量的特點很像將外部函數(shù)的變量直接“打包”到內部函數(shù)中一樣,我們也可以這樣理解閉包:將組成函數(shù)的語句以及執(zhí)行這些語句的環(huán)境“打包”在一起時得到的對象稱為閉包。

和閉包相關的幾個對象
為了了解閉包是怎么實現(xiàn)內部函數(shù)對外部函數(shù)變量的引用,還需要對閉包相關的幾個對象進行介紹。關于這幾個對象會涉及到Python的底層實現(xiàn),本文中對此不加以詳述,可以參考以下文章:

不過,為了直觀地說明閉包的實現(xiàn)過程(不分析底層實現(xiàn)),這里先簡單介紹以下code對象。code對象是指代碼對象,表示編譯成字節(jié)的的可執(zhí)行Python代碼,或者字節(jié)碼。它有幾個比較重要的屬性:

co_name:函數(shù)的名稱
co_nlocals: 函數(shù)使用的局部變量的個數(shù)
co_varnames: 一個包含局部變量名字的元組
co_cellvars: 是一個元組,包含嵌套的函數(shù)所引用的局部變量的名字
co_freevars: 是一個元組,保存使用了的外層作用域中的變量名
co_consts: 是一個包含字節(jié)碼使用的字面量的元組

其中比較關鍵的是co_varnames和co_freevars兩個屬性。我們對上面的例子稍加修改:

Python

            
>>> def foo():
    a = 1
    b = 2
    def bar():
      return a + 1
    def bar2():
      return b + 2
    return bar
>>> bar = foo()
# 外層函數(shù)
>>> foo.func_code.co_cellvars
('a', 'b')
>>> foo.func_code.co_freevars
()
# 內層嵌套函數(shù)
>>> bar.func_code.co_cellvars
()
>>> bar.func_code.co_freevars
('a',)

>>> def foo():
    a = 1
    b = 2
    def bar():
      return a + 1
    def bar2():
      return b + 2
    return bar
>>> bar = foo()
# 外層函數(shù)
>>> foo.func_code.co_cellvars
('a', 'b')
>>> foo.func_code.co_freevars
()
# 內層嵌套函數(shù)
>>> bar.func_code.co_cellvars
()
>>> bar.func_code.co_freevars
('a',)

          

以上說明外層函數(shù)的code對象的co_cellvars保存了內部嵌套函數(shù)需要引用的變量的名字,而內層嵌套函數(shù)的code對象的co_freevars保存了需要引用外部函數(shù)作用域中的變量名字。具體來說,就是foo函數(shù)中嵌套了兩個函數(shù),它們都需要引用foo函數(shù)局部作用域中的變量,所以foo.func_code.co_cellvars便包含變量a和變量b的名稱。而函數(shù)bar是foo的返回值,只引用了變量a,因此bar.func_code.co_freevars中便只包含變量a。

內部函數(shù)和外部函數(shù)的co_freevars、co_cellvars的對應關系,使得在函數(shù)編譯過程中內部函數(shù)具有了一個閉包的特殊屬性__closure__(底層中對此有相關實現(xiàn))。__closure__屬性是一個由cell對象組成的元組,包含了由多個作用域引用的變量。可以做以下驗證:

            
>>> foo.__closure__   #None
# 內部函數(shù)bar對變量a的引用
>>> bar.__closure__
(
            
              ,)
# 內部函數(shù)bar引用的變量a的值
>>> bar.__closure__[0].cell_contents
1
 
            
          

本文簡單講解了PYTHON的閉包,作用域的基本知識,如果想詳細了解,請在本站中查詢Python中的作用域規(guī)則和閉包詳解


更多文章、技術交流、商務合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 国产臀控福利视频在线 | 成人区精品一区二区毛片不卡 | 最新国产中文字幕 | 亚洲成a人伦理 | 日本不卡高清免费v | 国产成人国产在线观看入口 | 最新地址四虎www4hutv | 久久综合99re88久久爱 | 97视频精品 | 国产欧美一区二区三区久久 | 午夜毛片视频 | 久草在线资源 | 九九性视频| 色综合天天综合网亚洲 | 一级a性色生活片毛片 | 亚洲精品国产福利一区二区三区 | 欧美看片 | 国产成人cao在线 | 欧美成人黄色网 | www国产永久免费视频看看 | 国产午夜精品不卡视频 | 国产成人99精品免费视频麻豆 | 色色网视频 | 久久久精品免费国产四虎 | 国产精品9999 | sese在线播放 | 欧美真人视频一级毛片 | 4虎影院在线观看 | 亚洲国产成人最新精品资源 | 四虎国产精品永久在线 | 国产精品成人网 | 国产91在线|亚洲 | 五月婷婷丁香在线视频 | 久久综合久久自在自线精品自 | 亚洲一区精品中文字幕 | 日日爽夜夜爽 | 免费亚洲视频 | 欧美金8天国| 亚洲涩色 | 日日摸夜夜摸人人嗷嗷叫 | 久久午夜青青草原影院 |