python名稱空間

hiekay發表於2018-12-28

名稱空間,英文名字:namespaces

在研習名稱空間以前,請開啟在python的互動模式下,輸入:import this

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren`t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you`re Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it`s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let`s do more of those!

這裡看到的就是所謂《python之禪》,看最後一句: Namespaces are one honking great idea — let`s do more of those!

這是為了說明Namespaces、名稱空間值重要性。

什麼是名稱空間

從“一切皆為物件”開始說起吧。物件,很多時候我們直接使用它並不方便,因此要給它取一個名字。打個比方,有這樣一個物種,它是哺乳綱靈長目人科人屬智人種,這就是所謂的物件,但是,在平時提及這個物件的時候,總是要說“哺乳綱靈長目人科人屬智人種”,是不是太麻煩了?於是聰明的這個物種就為這個世界上的各種物件命名,例如將“哺乳綱靈長目人科人屬智人種”這個物件命名為“人”。

在程式設計中也是如此,前面在講述變數相關知識的時候已經說明了變數和引用物件的關係。

>>> a = 7
>>> id(7)
137589400
>>> id(a)
137589400
>>> id(7)==id(a)
True

看這個例子。7就是一個計算機記憶體中存在的物件,用id()這個內建函式可以檢視7在記憶體(在RAM)中的地址。a 就是為這個物件預備的名字,如前面所講的,它與記憶體中的一個編號為137589400的物件關聯,或者說引用了這個物件,這個物件就是7.

如果做了下面的操作:

>>> a = a+1
>>> id(a)
137589388
>>> a
8
>>> id(8)
137589388

其實,上面操作中的a+1完成的是a引用的物件7+1,只不過是順著物件7的命名a匯入了物件7罷了,這樣就在記憶體中建立了一個新的物件8,同樣通過id()函式檢視到記憶體中的地址,通過地址可以看到,這時候的a又自動引用物件8了.

>>> id(7)   #物件7在記憶體中的地址沒變
137589400
>>> b = 7   #b引用此物件
>>> id(b)   
137589400

上面a轉換引用物件的過程,是自動完成的。而當b=7的時候,並不是在記憶體中從新建立一個物件7,而是b引用了已有的物件。這就是python的所謂動態語言的特點。

image.png

當然,可以給任何物件取名字,或者說為任何物件都可以建立一個所引用的變數。比如函式、類都可以,此處不贅述,前面已經多次用到了。

現在已經又一次明確了,每個名稱(命名)——英文中的NAME有動詞和名字兩種,所以,由於中文的特點,似乎怎麼說都可以,只要明白所指,因為中文是強調語境的語言——都與某個物件有對應關係。那麼所謂的名稱空間,就是這些命名(名稱)的集合,它們分別與相應的物件有對應關係。

用一句比較學術化的語言說:

名稱空間是從所定義的命名到物件的對映集合。

不同的名稱空間,可以同時存在,當彼此相互獨立互不干擾。

名稱空間因為物件的不同,也有所區別,可以分為如下幾種:

  • 內建名稱空間(Built-in Namespaces):Python執行起來,它們就存在了。內建函式的名稱空間都屬於內建名稱空間,所以,我們可以在任何程式中直接執行它們,比如前面的id(),不需要做什麼操作,拿過來就直接使用了。
  • 全域性名稱空間(Module:Global Namespaces):每個模組建立它自己所擁有的全域性名稱空間,不同模組的全域性名稱空間彼此獨立,不同模組中相同名稱的名稱空間,也會因為模組的不同而不相互干擾。
  • 本地名稱空間(Function&Class: Local Namespaces):模組中有函式或者類,每個函式或者類所定義的名稱空間就是本地名稱空間。如果函式返回了結果或者丟擲異常,則本地名稱空間也結束了。

上述三種名稱空間的關係

image.png

那麼程式在查詢上述三種名稱空間的時候,就按照從裡到外的順序,即:Local Namespaces –> Global Namesspaces –> Built-in Namesspaces

還要補充說一下,既然名稱空間中存在著命名和物件的對映,是“鍵值”對應的,例如:{“name”:”hiekay”,”lang”:”python”}

>>> def foo(num,str):
...     name = "hiekay"
...     print locals()
... 
>>> foo(2,"hiekay.github.io")
{`num`: 2, `name`: `hiekay`, `str`: `hiekay.github.io`}
>>> 

這是一個訪問本地名稱空間的方法,用print locals() 完成,從這個結果中不難看出,所謂的名稱空間中的資料儲存結構和dictionary是一樣的。

根據習慣,如果訪問全域性名稱空間,可以使用 print globals()。

作用域

作用域是指 Python 程式可以直接訪問到的名稱空間。“直接訪問”在這裡意味著訪問名稱空間中的命名時無需加入附加的修飾符。

程式也是按照搜尋名稱空間的順序,搜尋相應空間的能夠訪問到的作用域。

def outer_foo():
    b = 20
    def inner_foo():
        c = 30
a = 10

加入我現在位於inner_foo()函式內,那麼c對我來講就在本地作用域,而b和a就不是。如果我在inner_foo()內再做:b=50,這其實是在本地名稱空間內新建立了物件,和上一層中的b=20毫不相干。可以看下面的例子:

#!/usr/bin/env python
#coding:utf-8

def outer_foo():
    a = 10
    def inner_foo():
        a = 20
        print "inner_foo,a=",a      #a=20

    inner_foo()
    print "outer_foo,a=",a          #a=10

a = 30
outer_foo()
print "a=",a                #a=30

#執行結果

inner_foo,a= 20
outer_foo,a= 10
a= 30

如果要將某個變數在任何地方都使用,且能夠關聯,那麼在函式內就使用global 宣告,其實就是曾經講過的全域性變數。


相關文章