你真的知道什麼是 Python「名稱空間」嗎?

Python空間發表於2019-03-01

寫在之前

名稱空間,又名 namesapce,是在很多的程式語言中都會出現的術語,估計很多人都知道這個詞,但是讓你真的來說這是個什麼,估計就歇菜了,所以我覺得「名稱空間」 有必要了解一下。

全域性變數 & 區域性變數

全域性變數和區域性變數是我們理解名稱空間的開始,我們先來看一段程式碼:

x = 2
def func():
   x = 3
   print('func x ---> ',x)

func()
print('out of func x ---> ',x)
複製程式碼

這段程式碼輸出的結果如下:

func x ---> 3
out of func x ---> 2
複製程式碼

從上述的結果中可以看出,執行 func(),輸出的是 func() 裡面的變數 x 所引用的物件 3,之後執行的是程式碼中的最後一行。這裡要區分清楚,前一個 x 輸出的是函式內部的變數 x,後一個 x 輸出的是函式外的變數 x,兩個變數互相不影響,在各自的作用域中起作用。

那個只在函式內起作用的變數就叫「區域性變數」,有了「區域性」就有相應的 「全部」,但是後者聽起來有歧義,所以就叫了「全域性」。

x = 2
def func():
   global x = 3 #注意此處
   print('func x ---> ',x)

func()
print('out of func x ---> ',x)
複製程式碼

這段程式碼中比上段程式碼多加了一個 global x,這句話的意思是在宣告 x 是全域性變數,通俗點說就是這個 x 和 函式外的 x 是同一個了,所以結果就成了下面這樣:

func x ---> 3
out of func x ---> 3
複製程式碼

這樣乍一看好像全域性變數好強,可以管著函式內外,但是我們還是要注意,全域性變數還是謹慎使用的好,因為畢竟內外有別,不要帶來混亂。

作用域

作用域,用比較直白的方式來說,就是程式中變數與物件存在關聯的那段程式,比如我在上面說的, x = 2 和 x = 3 是在兩個不同的作用域中。

通常的,作用域是被分為靜態作用域和動態作用域,雖然我們說 Python 是動態語言,但是它的作用域屬於靜態作用域,即 Python 中的變數的作用域是由該變數所在程式中的位置所決定的。

在 Python 中作用域被劃分成四個層級,分別是:local(區域性作用域),enclosing(巢狀作用域),global(全域性作用域)和 built - in(內建作用域)。對於一個變數,Python 也是按照之前四個層級依次在不用的作用域中查詢,我們在上一段程式碼中,對於變數 x,首先搜尋的是函式體內的區域性作用域,然後是函式體外的全域性作用域,至於這段話具體怎麼來理解,請看下面的例子:

def out_func():
   x = 2
   def in_func():
       x = 3
       print('in_func x ---> ',x)
   in_func()
   print('out_func x ---> ',x)

x = 4
out_func()
print('x == ',x)
複製程式碼

上述程式碼執行的結果是:

in_func x ---> 3
out_func x ---> 2
x == 4
複製程式碼

仔細觀察一下上面的程式碼和執行的結果,你就會發現變數在不同的範圍內進行搜尋的規律,是不是感覺這些都是以前被你忽略的呢?

名稱空間

《維基百科》中說「名稱空間是對作用域的一種特殊的抽象」,在這裡我用一個比方來具體說明一下:

比如張三在公司 A,他的工號是 111,李四在公司 B,他的工號也是 111,因為兩個人在不同的公司,他們倆的工號可以相同但是不會引起混亂,這裡的公司就表示一個獨立的名稱空間,如果兩個人在一個公司的話,他們的工號就不能相同,否則光看工號也不知道到底是誰。

其實上面舉的這個例子的特點就是我們使用名稱空間的理由,在大型的計算機程式中,往往會出現成百上千的識別符號,名稱空間提供隱藏區域識別符號的機制。通過將邏輯上相關的識別符號構成響應的名稱空間,可以使整個系統更加的模組化。

我在開頭引用的《維基百科》的那句話說 「名稱空間是對作用域的一種特殊的抽象」,它其實包含了處於該作用域內的識別符號,且它本身也用一個識別符號來表示。在 Python 中,名稱空間本身的識別符號也屬於更外層的一個名稱空間,所以名稱空間也是可以巢狀的,它們共同生活在「全域性名稱空間」下。

簡言之,不同的名稱空間可以同時存在,但是彼此獨立,互不干擾。當然了,名稱空間因為其物件的不同也有所區別,可以分為以下幾種:

  • 本地名稱空間:模組中有函式或者類的時候,每個函式或者類所定義的名稱空間即是本地名稱空間,當函式返回結果或者丟擲異常的時候,本地名稱空間也就結束了。

  • 全域性名稱空間:每個模組建立了自己所擁有的全域性名稱空間,不同模組的全域性名稱空間彼此獨立,不同模組中相同名稱的名稱空間也會因為模組的不同而不相互干擾。

  • 內建名稱空間:當 Python 執行起來的時候,它們就存在了,內建函式的名稱空間都屬於內建名稱空間,所以我們可以在任何程式中直接執行它們。

程式查詢名稱空間的時候也有一套順序,依次按照本地名稱空間 ,全域性名稱空間,內建名稱空間。

def fun(like):
   name = 'rocky'
   print(locals())

fun('python')
複製程式碼

訪問本地名稱空間使用 locals 完成,我們來看一下結果:

{'name': 'rocky', 'like': 'python'}
複製程式碼

從上面的結果中可以看出,名稱空間中的資料儲存的結構和字典是一樣的。可能你已經猜到了,當我們要訪問全域性名稱空間的時候,可以使用 globals。

關於名稱空間還有一個生命週期的問題,就是一個名稱空間什麼時候出現,什麼時候消失,這個很好理解,就是哪部分被讀入記憶體,哪部分的名稱空間就存在了,比如我們在上面說的,Python 啟動,內建名稱空間就建立。

寫在之後

更多內容,歡迎關注公眾號「Python空間」,期待和你的交流。

在這裡插入圖片描述

相關文章