天天看點

python 命名空間

命名空間,英文名字: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的所謂動态語言的特點。

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

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

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

命名空間是從所定義的命名到對象的映射集合。

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

命名空間因為對象的不同,也有所差別,可以分為如下幾種:

  • 内置命名空間(Built-in Namespaces):Python運作起來,它們就存在了。内置函數的命名空間都屬于内置命名空間,是以,我們可以在任何程式中直接運作它們,比如前面的id(),不需要做什麼操作,拿過來就直接使用了。
  • 全局命名空間(Module:Global Namespaces):每個子產品建立它自己所擁有的全局命名空間,不同子產品的全局命名空間彼此獨立,不同子產品中相同名稱的命名空間,也會因為子產品的不同而不互相幹擾。
  • 本地命名空間(Function&Class: Local Namespaces):子產品中有函數或者類,每個函數或者類所定義的命名空間就是本地命名空間。如果函數傳回了結果或者抛出異常,則本地命名空間也結束了。

上述三種命名空間的關系

那麼程式在查詢上述三種命名空間的時候,就按照從裡到外的順序,即: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 聲明,其實就是曾經講過的全局變量。