詳解python命名空間和作用域


1、典型案例

先從幾個典型的案例來看下名稱空間及作用域對python代碼運行的影響,請看下面幾個代碼實例及其執行結果,是否符合你的預期。

代碼1:塊作用域

if True:
    i = 1
print i

執行結果: 1

 

代碼2:函數作用域

def f():
    i = 1

f()
print i

執行結果:執行報錯“NameError: name 'cc' is not defined”

 

代碼3:靜態作用域

i = 1
def test():
    i += 1

test()

執行結果:執行報錯“UnboundLocalError: local variable 'i' referenced before assignment”

 

代碼4:靜態作用域

i = 1
def g():
    print i
    
def f():
    i = 2
    g()
f() 

執行結果: 1

 

代碼5:閉包、全局作用域

i = 1
def f():
    i = 2
    def g():
        print i
    return g


func = f()
func()
print i

執行結果:2    1 

PS:python2.1之前執行該代碼會報錯,閉包(嵌套作用域)是在之后引入的概念,內部函數g()只能訪問本函數中的命名空間、全局命名空間、內建命名空間,無法訪問外部調用函數的命名空間。

 

2、命名空間

【定義】

    名稱到對象的映射。命名空間是一個字典的實現,鍵為變量名,值是變量對應的值。各個命名空間是獨立沒有關系的,一個命名空間中不能有重名,但是不同的命名空間可以重名而沒有任何影響。

 

【分類】

    python程序執行期間會有2個或3個活動的命名空間(函數調用時有3個,函數調用結束后2個)。按照變量定義的位置,可以划分為以下3類:

    Local,局部命名空間,每個函數所擁有的命名空間,記錄了函數中定義的所有變量,包括函數的入參、內部定義的局部變量。

    Global,全局命名空間,每個模塊加載執行時創建的,記錄了模塊中定義的變量,包括模塊中定義的函數、類、其他導入的模塊、模塊級的變量與常量。

    Built-in,python自帶的內建命名空間,任何模塊均可以訪問,放着內置的函數和異常。

 

【生命周期】

    Local(局部命名空間)在函數被調用時才被創建,但函數返回結果或拋出異常時被刪除。(每一個遞歸函數都擁有自己的命名空間)。

    Global(全局命名空間)在模塊被加載時創建,通常一直保留直到python解釋器退出。

    Built-in(內建命名空間)在python解釋器啟動時創建,一直保留直到解釋器退出。

   

    各命名空間創建順序:python解釋器啟動 ->創建內建命名空間 -> 加載模塊 -> 創建全局命名空間 ->函數被調用 ->創建局部命名空間

    各命名空間銷毀順序:函數調用結束 -> 銷毀函數對應的局部命名空間 -> python虛擬機(解釋器)退出 ->銷毀全局命名空間 ->銷毀內建命名空間

    python解釋器加載階段會創建出內建命名空間、模塊的全局命名空間,局部命名空間是在運行階段函數被調用時動態創建出來的,函數調用結束動態的銷毀的。

 

3、作用域

【定義】

    作用域是針對變量而言,指申明的變量在程序里的可應用范圍。或者稱為變量的可見性。

 

【分類】

    只有函數、類、模塊會產生作用域,代碼塊不會產生作用域(參考代碼1)。作用域按照變量的定義位置可以划分為4類:

    Local(函數內部)局部作用域

    Enclosing(嵌套函數的外層函數內部)嵌套作用域(閉包)

    Global(模塊全局)全局作用域

    Built-in(內建)內建作用域

 

【規則】

    1、靜態作用域規則

         定義:python中變量的作用域是由它在源代碼中的位置決定的。(名字查找是動態發生的)

         說明:參考代碼3/4,以代碼3為例說明,在模塊中定義了一個全局變量 i = 1,在test方法中執行 i += 1,對變量 i進行了賦值動作,該賦值動作決定了i在test()方法中是一個局部變量, i += 1可以拆分為兩步執行,首先執行 i + 1, 然后將結果賦值給i。執行i + 1操作時,i雖然申明為局部變量,但是沒有綁定任何具體值,因此報錯。

    2、最內嵌套作用域規則

         定義:由一個賦值語句引進的名字在這個賦值語句所在的作用域里是可見(起作用)的,而且在其內部嵌套的每個作用域內也可見,除非它被嵌套於內部的且引進同樣名字的賦值語句所遮蔽。

         說明:參考代碼5. 方法g()是方法f()中定義的內嵌函數。在方法f()中定義的局部變量 i =2,在內嵌方法 g()中是可見的。如果在g()中又定義一個重名的變量 i = 3,則f()中定義的變量將被遮蔽。

 

4、命名空間與作用域的關系

    命名空間定義了在某個作用域內變量名和綁定值之間的對應關系,命名空間是鍵值對的集合,變量名與值是一一對應關系。作用域定義了命名空間中的變量能夠在多大范圍內起作用。

    命名空間在python解釋器中是以字典的形式存在的,是以一種可以看得見摸得着的實體存在的。作用域是python解釋器定義的一種規則,該規則確定了運行時變量查找的順序,是一種形而上的虛的規定。

 

【變量查找法則】

    python解釋器動態執行過程中,對遇到的變量進行解釋時,是按照一條固定的作用域鏈查找解釋的,又稱為LEGB法則

    其中L代表Local 局部作用域,E代表Enclosing 嵌套作用域,G代表Global 全局作用域,B代表Built-in 內建作用域。

    python解釋器查找變量時,會按照順序依次查找局部作用域,嵌套作用域,全局作用域,內建作用域,在任意一個作用域中找到變量則停止查找,所有作用域查找完成沒有找到對應的變量,則拋出 NameError: name 'xxxx' is not defined的異常。

    在局部作用域中,可以看到局部作用域、嵌套作用域、全局作用域、內建作用域中所有定義的變量。

    在全局作用域中,可以看到全局作用域、內建作用域中的所有定義的變量,無法看到局部作用域中的變量。

            

 

【參考資料】

http://www.cnblogs.com/windlaughing/archive/2013/05/26/3100362.html
http://blog.csdn.net/cc7756789w/article/details/46635383
http://www.cnblogs.com/livingintruth/p/3296010.html
 
《Python源碼剖析》[中][電子工業出版社] 陳儒
《Python核心編程 第二版 》[美][人民郵電出版社] Wesley J.Chun


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM