當前位置:
首頁 > 最新 > 由一個例子到 python 的名字空間

由一個例子到 python 的名字空間

例子引入

例1

#!/usr/bin/env python

# encoding: utf-8

deffunc1():

x=1

printglobals()

print before func1: ,locals()

deffunc2():

a=1

print before fun2: ,locals()

a+=x

print after fun2: ,locals()

func2()

print after func1: ,locals()

printglobals()

if__name__== __main__ :

func1()

可以正常輸出結果:並且需要注意,在func2使用x變數之前的名字空間就已經有了 x :1.

beforefunc1:{ x :1}

beforefun2:{ a :1, x :1}

afterfun2:{ a :2, x :1}

afterfunc1:{ x :1, func2 : }

稍微改一點:如下

例2:

#!/usr/bin/env python

# encoding: utf-8

deffunc1():

x=1

print before func1: ,locals()

deffunc2():

print before fun2: ,locals()

x+=x#就是這裡使用x其餘地方不變

print after fun2: ,locals()

func2()

print after func1: ,locals()

if__name__== __main__ :

func1()

輸出就開始報錯: 而且在before func2也沒有了x.

beforefunc1:{ x :1}

beforefun2:{}

Traceback(most recent calllast):

File"test.py",line18,in

func1()

File"test.py",line14,infunc1

func2()

File"test.py",line11,infunc2

x+=x

UnboundLocalError:localvariable x referenced beforeassignment

這兩個例子正好涉及到了python裡面最核心的內容:名字空間,正好總結一下,然後在解釋這幾個例子。

名字空間(Namespace)

比如我們定義一個」變數」

In[14]:a

NameError:name a isnotdefined

所以,這裡更準確的叫法應該是名字。 一些語言中比如c,c++,java 變數名是內存地址別名, 而Python 的名字就是一個字元串,它與所指向的目標對象關聯構成名字空間裡面的一個鍵值對,因此可以這麼說,python的名字空間就是一個字典.。

分類

python裡面有很多名字空間,每個地方都有自己的名字空間,互不干擾,不同空間中的兩個相同名字的變數之間沒有任何聯繫一般有4種:LEGB四種

locals: 函數內部的名字空間,一般包括函數的局部變數以及形式參數

enclosing function: 在嵌套函數中外部函數的名字空間, 對fun2來說,fun1的名字空間就是。

globals: 當前的模塊空間,模塊就是一些py文件。也就是說,globals()類似全局變數。

__builtins__: 內置模塊空間,也就是內置變數或者內置函數的名字空間。

當程序引用某個變數的名字時,就會從當前名字空間開始搜索。搜索順序規則便是: LEGB.

locals -> enclosing function -> globals -> __builtins

一層一層的查找,找到了之後,便停止搜索,如果最後沒有找到,則拋出在NameError的異常。這裡暫時先不討論賦值操作。

比如例1中的a = x + 1這行代碼,需要引用x, 則按照LEGB的順序查找,locals()也就是func2的名字空間沒有,進而開始E,也就是func1,裡面有,找到了,停止搜索,還有後續工作,就是把x也加到自己的名字空間,這也是為什麼fun2的名字空間裡面也有x的原因。

訪問方式

其實上面都已經說了,這裡暫時簡單列一下

使用locals()訪問局部命名空間

使用globals()訪問全局命名空間

這裡有一點需要注意,就是涉及到了from A import B和import A的一點區別。

#!/usr/bin/env python

# encoding: utf-8

importcopy

fromcopyimportdeepcopy

deffunc():

x=123

print func locals: ,locals()

s= hello world

if__name__== __main__ :

func()

print globals: ,globals()

輸出結果:

func locals:{ x :123}

globals:{ __builtins__ : ,

__file__ : test.py ,

__package__ :None,

s : hello world ,

func : ,

deepcopy : ,

__name__ : __main__ ,

copy : ,

__doc__ :None}

從輸出結果可以看出globals()包含了定義的函數,變數等。對於 deepcopy : 可以看出deepcopy已經被導入到自己的名字空間了,而不是在copy裡面。而導入的import copy則還保留著自身的名字空間。因此要訪問copy的方法,就需要使用copy.function了。這也就是為什麼推薦使用import module的原因,因為from A import B這樣會把B引入自身的名字空間,容易發生覆蓋或者說污染。

生存周期

每個名字空間都有自己的生存周期,如下:

__builtins__: 在python解釋器啟動的時候,便已經創建,直到退出

globals: 在模塊定義被讀入時創建,通常也一直保存到解釋器退出。

locals : 在函數調用時創建, 直到函數返回,或者拋出異常之後,銷毀。另外遞歸函數每一次均有自己的名字空間。

看著沒有問題,但是有很多地方需要考慮。比如名字空間都是在代碼編譯時期確定的,而不是執行期間。這個也就可以解釋為什麼在例1中,before func2:的locals()裡面包含了x: 1這一項。再看下面這個,

deffunc():

ifFalse:

x=10#該語句永遠不執行

printx

肯定會報錯的,但是錯誤不是

NameError: global name x is not defined

而是:

UnboundLocalError: local variable x referenced before assignment

雖然x = 10永遠不會執行,但是在執行之前的編譯階段,就會把x作為locals變數,但是後面編譯到print的時候,發現沒有賦值,因此直接拋出異常,locals()裡面便不會有x。這個就跟例子2中,before func2裡面沒有x是一個道理。

賦值

為什麼要把賦值單獨列出來呢,因為賦值操作對名字空間的影響很大,而且很多地方需要注意。

核心就是:賦值修改的是命名空間,而不是對象, 比如:

a = 10

這個語句就是把a放入到了對應的命名空間, 然後讓它指向一個值為10的整數對象。

a=[]

a.append(1)

這個就是把a放入到名字空間,然後指向一個列表對象,然而後面的a.append(1)這句話只是修改了list的內容,並沒有修改它的內存地址。因此

並沒有涉及到修改名字空間。

賦值操作有個特點就是: 賦值操作總是在最裡層的作用域.也就說,只要編譯到了有賦值操作,就會在當前名字空間內新創建一個名字,然後開始才綁定對象。即便該名字已存在於賦值語句發生的上一層作用域中;

總結

分析例子

現在再看例子2, 就清晰多了, x += x 編譯到這裡時,發現了賦值語句,於是準備把x新加入最內層名字空間也就是func2中,即使上層函數已經存在了; 但是賦值的時候,又要用到x的值, 然後就會報錯:

UnboundLocalError: local variable x referenced before assignment

這樣看起來好像就是 內部函數只可以讀取外部函數的變數,而不能做修改,其實本質還是因為賦值涉及到了新建locals()的名字。

在稍微改一點:

#!/usr/bin/env python

# encoding: utf-8

deffunc1():

x=[1,2]

print before func1: ,locals()

deffunc2():

print before fun2: ,locals()

x[]+=x[]#就是這裡使用x[0]其餘地方不變

print after fun2: ,locals()

func2()

print after func1: ,locals()

if__name__== __main__ :

func1()

這個結果就是:

beforefunc1:{ x :[1,2]}

beforefun2:{ x :[1,2]}

afterfun2:{ x :[2,2]}

afterfunc1:{ x :[2,2], func2 : }

咋正確了呢—這不應該要報錯嗎? 其實不然,就跟上面的a.append(1)是一個道理。

x[0] += x[0] 這個並不是對x的賦值操作。按照LEGB原則, 搜到func1有變數x並且是個list, 然後將其加入到自己的locals(), 後面的x[0] += x[0], 就開始讀取x的元素,並沒有影響func2的名字空間。另外無論func1與func2的名字空間的x 沒有什麼關係,只不過都是對[1, 2]這個列表對象的一個引用。

這個例子其實也給了我們一個啟發,我們知道內部函數無法直接修改外部函數的變數值,如例2,如果藉助list的話, 就可以了吧!比如把想要修改的變數塞到一個list裡面,然後在內部函數裡面做改變!當然python3.x裡面有了nonlocal關鍵字,直接聲明一下就可以修改了。看到這裡,對作用域理解應該有一點點了吧。

延伸

與閉包的不同

我們都知道閉包是把外部函數的值放到func.func_closure裡面,為什麼不像上面的例子一樣直接放到函數的名字空間呢?

這是因為locals()空間是在函數調用的時候才創建! 而閉包只是返回了一個函數, 並沒有調用,也就沒有所謂的空間。

locals()與globals()

在最外層的模塊空間里locals()就是globals()

In[2]:locals()isglobals()

Out[2]:True

另外我們可以手動修改globals()來創建名字

In[3]:globals()[ a ]= abcde

In[4]:a

Out[4]: abcde

但是locals()在函數裡面的話, 貌似是不起作用的,如下:

In[5]:deffunc():

...:x=10

...:printx

...:printlocals()

...:locals()[ x ]=20

...:printx

In[6]:func()

10

{ x :10}

10

這是因為解釋器會將 locals 名字複製到 一個叫FAST的 區域來優化訪問速度,而實際上解釋器訪問對象時,是從FAST區域裡面讀取的,而非locals()。所以直接修改locals()並不能影響x(可以使用exec 動態訪問,不再細述)。 不過賦值操作,會同時刷新locals()和FAST區域。

查了不少資料,說了這麼多,我只想說,作為python最核心的東西,名字空間還有很多很多地方需要探究,比如

作用域(scope)與名字空間, 這裡只是模糊了二者的區別

面向對象,也就是類的名字空間, 又有不一樣的地方。。。

來源:shomy

鏈接:shomy.top/2016/03/01/python-namespace-1/

程序員共讀整理髮布,轉載請聯繫作者獲得授權。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序員共讀 的精彩文章:

無休止加班的成因
9個實戰及面試常用Shell腳本編寫
那些讓程序員水深火熱的境況……
Swift開發之String 還是 NSString
詳解Linux命令:改變文件或目錄的訪問許可權

TAG:程序員共讀 |

您可能感興趣

《談判官》楊冪戲裡的現代時尚空間,人均不到200元就可體驗Check out this private PentHouse
Keepland:一個健身空間的新使命
C命名空間(Namespace)
Femme Fanatique一個致力於探戈世界鞋店空間
新iPhone不僅支持Apple Pencil,還有大儲存空間!
小空間如何「對鏡貼花黃」?教你6個妙點子#fashion課堂#
IPhone 小技巧:為什麼iPhone 相冊全部刪除了,卻還佔了好幾個G 存儲空間?
首爾projekt produkt眼鏡零售空間設計
Unity 2018.1 beta 發布,支持谷歌空間音頻和Magic Leap SDK
Leap Motion分享《Cat Explorer》空間交互設計細節
Leap Motion 分享《Cat Explorer》空間交互設計細節
iPhoneXS為啥要出512G的?這些手游比端游占的空間還要大!
Norm Architects設計的一間可以作為合作空間的菜單的展示廳
Stephen Shore:把空洞和立體放進二維空間
Visceral構想的《死亡空間4》原來是這樣一款遊戲!
SharePoint空間為Microsoft Office帶來混合現實
上市首日股價大漲 拼多多如何構建Costco+Disney空間?
法國老佛爺百貨打造的藝術創意空間 Lafayette Anticipations 3月10日正式開放
小型工作空間!洛杉磯設計團隊 Knowhow Shop 新作「lighthouse」
8㎡,6個空間,獨居女生的雙層loft