程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

自學Python 34 命名空間:存儲變量與值對應關系的字典

編輯:Python

Python命名空間


文章目錄

  • Python命名空間
  • 一、命名空間的本質
  • 二、查找命名空間
  • 三、命名空間的生命周期
  • 四、命名空間訪問函數locals()與globals()


在現實應用中,我們可以將Python 語言中的命名空間理解為一個容器。在這個容器中可以裝許多標識符,不同容器中同名的標識符是不會相互沖突的,它們有相互的對應關系。在Python程序中,使用命名空間來記錄變量的軌跡。命名空間是一個字典(Dictionary),它的鍵就是變量名,它的值就是對應變量的值。


一、命名空間的本質

在Python程序中,通常會存在如下三個可用的命名空間。
(1)每個函數都有自己的命名空間,這被稱為局部命名空間,它記錄了函數的變量,包括函數的參數和局部定義的變量。
(2)每個模塊擁有自己的命名空間,這被稱為全局命名空間,它記錄了模塊的變量,包括函數、類、其他導入的模塊、模塊級的變量和常量。
(3)還有就是內置命名空間,任何模塊均可訪問它,它存放著內置的函數和異常。
要想理解 Python語言的命名空間,首先需要掌握如下所示的三條規則。
(1)賦值(包括顯式賦值和隱式賦值)產生標識符,賦值的地點決定標識符所處的命名空間。
(2)函數定義(包括def和 lambda)產生新的命名空間。
(3) Python搜索一個標識符的順序是“LEGB”。所謂的“LEGB”,是指Python語言中4層命名空間的英文名字首字母的縮寫,具體說明如下所示。
●最裡面的1層是L (local),表示在一個函數定義中,而且在這個函數裡面沒有再包含函數的定義。
●第2層E (enclosing functio), 表示在一個函數定義中,但這個函數裡面還包含有函數的定義,其實L層和E層只是相對的。
●第3層G (global) ,是指一個模塊的命名空間,也就是說在一個.py文件中定義的標識符,但不在一個函數中。
●第4層B (builtin) ,是指Pylo解釋器啟動時就已經具有的命名空間,之所以叫builtin是因為在Python解釋器啟動時會自動載入__builtin__模塊,這個模塊中的list、str 等內置函數處於B層的命名空間中。
注意:在Python程序中,可以通過模塊來管理復雜的程序,而將不同功能的函數分布在不同的模塊中,函數及其全局命名空間決定了函數中引用全局變量的值。函數的全局命名空間始終是定義該函數的模塊,而不是調用該函數的命名空間。因此,在函數中引用的全局變量始終是定義該函數模塊中的全局變量。
例如在下面的實例代碼中,演示了函數與其全局命名空間關系的過程。實例文件mo.py是一個模塊文件,在裡面定義了全局變重name和函數moo_fun(),並在函數moo_fun()中輸出了全局變量name 的值。文件mo.py 的具體實現代碼如下所示。

name= "Moo Module" #定義變量name的初始值
def moo_fun(): #定義方法moo fun()
print('函數moo_fun: ') #打印顯示文本
print('變量name: ',name) #打印顯示變量name的值

實例文件test.py是一個測試文件,調用了模塊mo中的方法moo_fun()。在此文件中也定義了全局變量name和函數 bar(),並在函數bar()中輸出了全局變量name 的值。然後分別調用本模塊中定義的函數 bar()和從mo模塊中導入的函數moo_fun(),最後還定義一個把函數作為參數傳入並調用的函數call_moo_fun()。因為函數中引用的全局變量始終是定義該函數模塊中的全局變量,所以第一次調用輸出了當前模塊中的全局變量name的值;而第二次調用從mo模塊中導入的函數moo_fun(),輸出的則是在mo模塊中的全局變量name的值。第三次調用call_moo_fun()函數,並把從mo模塊中導入的函數moo_fun()作為參數傳入其中進行調用,即使是在函數內部被調用,它仍然輸出函數moo_fun()模塊中全局變量name的值。實例文件test.py 的具體實現代碼如下所示。

from mo import moo_fun #調用模塊mo中的方法moo fun ()#定義全局變裡name
name='current module'
def bar(): #定義函數bar()
print ('當前模塊中函數bar: ') #打印顯示文本
print ('變量name: ', name) #打印顯示變量name的值
def call_moo_fun (fun) : #定義方法cal1_moo_fun ()
fun ()
if __name__== '__main__': #輸出當前模塊中的全局變量name的值
bar()
print() #調用從mo模塊中行入的函數moo_fun
moo_fun ()
print()
call_moo_fun (moo_fun)

在運行程序後,第1次輸出的是當前模塊中的全局變量name的值’current module’,第2次輸出的是mo模塊中的全局變量name的值“Moo Module”,第3次輸出的仍然是mo模塊中的全局變量name的值“Moo Module”。執行後會輸出:

二、查找命名空間

在Python程序中,當某一行代碼要使用變量x的值時,會到所有可用的名字空間去查找這個變量,按照如下所示的順序進行查找。
(1)局部命名空間:特指當前函數或類的方法。如果函數定義了一個局部變量x,或一個參數x,Python程序將便用它,然後停止搜索。
(2)全局命名空間:特指當前的模塊。如果模塊定義了一個名為x的變量,函數或類,Python將使用它然後停止搜索。
(3)內置命名空間:對每個模塊都是全局的。作為最後的嘗試,Python將假設x是內置函數或變量。
(4)如果Python在上述命名空間找不到x,它將放棄查找並引發一個NameError異常,例如 NameError: name ‘aa’ is not defined。
在Python程序中,嵌套函數命名空間的杳找順序比較特殊,具體說明如下所示。
(1)先在當前(嵌套的或lambda)函數的命名空間中搜索
(2)然後在父函數的命名空間中進行搜索。
(3)接著在模塊命名空間中搜索。
(4)最後在內置命名空間中搜索。
例如在下面的實例代碼中,演示了嵌套函數命名空間的查找過程。

info="2024奧運會主辦地:" #定義全局變量的初始值
def func_father (country): #定義嵌孕函數func_father()
def func_son(area): #此處的city變量,覆蓋了父函數的city變重
city="巴黎"
print(info + country + city + area)
city="洛杉矶"
func_son("台區"); #調用內部函
func_father("法國")

在上述實例代碼中,info在全局命名空間中,country在父函數的命名空間中,city和 area在自己函數的命名空間中。執行後會輸出:

三、命名空間的生命周期

在Python程序中,在不同的時刻創建不同的命名空間,這些命名空間會有不同的生存期。具體說明如下所示。
(1)內置命名空間在Python解釋器啟動時創建,會一直保留下去,不會被刪除。
(2)模塊的全局命名空間在模塊定義被讀入時創建,通常模塊命名空間也會一直保存到解釋器退出。
(3)當函數被調用時創建一個局部命名空間當函數返回結果或拋出異常時被刪除。每一個遞歸調用的函數都擁有自己的命名空間。
Python語言有一個自己的特別之處,在於其賦值操作總是在最裡層的作用域。賦值不會復制數據,只是將命名綁定到對象而已。刪除操作也是如此,例如“del y”只是從局部作用域的命名空間中刪除命名“y”而已。而事實上,所有引入新命名的操作都作用於局部作用域。請看下面的演示代碼,因為在創建命名空間時,Python 會檢查代碼並填充局部命名空間。在並把它添加到局部命名空間中。當函數執
Python運行那行代碼之前,就發現了對i的賦值行時,Python解釋器認為i在局部命名空間中,但是沒有值,所以會產生錯誤。

i=1
def func2():
i=i+1
func2();

執行結果報錯:

四、命名空間訪問函數locals()與globals()

在Python程序中訪問命名空間時,不同的命名空間用不同的方式進行訪問,具體說明如下所示。
(1)局部命名空間。
在Python程序中可以使用內置函數locals()來訪問局部命名空間。例如在下面的實例命名代碼中,演示了使用內置函數 locals()來訪問局部命名空間的過程。
具體實現代碼如下所示。

def funcl(i,str): #定義函數func1 ()
X=1234 #定義變量x的值是12345
print(locals()) #訪問局部命名空間
funcl(1,"firs")

執行後會輸出:

(2)全局(模塊級別)命名空間。
在Python程序中,可以使用內置函數globals()來訪問全局(模塊級別)命名空間。
例如在下面的實例代碼中,演示了使用內置函數globals()來訪問全局命名空間的過程。

import copy #導入copy模塊
from copy import deepcopy #導入deepcopy
gstr = "global string" #定義變量gstr
def funcl(i,info): #定義函數func1 ()
x = 12345
print(locals()) #訪問局部命名空間
funcl(1,"first")
if __name__=="__main__":
print("the current scope's global variablest:")
dictionary=globals() #訪問全局(模塊級別)命名空間
print (dictionary)

執行後的效果如下圖所示,在此需要注意,執行效果中的某些輸出結果會因用戶的測試環境的差異而不同。

注意:通過上述執行效果可知,模塊的名字空間不僅僅包含模塊級的變量和常量,還包括所有在模塊中定義的函數和類。除此以外,還包括任何被導入到模塊中的東西。另外也可以看到,內置命名也同樣被包含在一個模塊中, 它被稱作__builtins__ 。當使用import module時,模塊自身被導入,但是它保持著自已的名字空間,這就是為什麼需要使用模塊名來訪問它的函數或屬性module.fumction的原因。但是當使用from module import function時,實際上是從另一個模塊中將指定的函數和屬性導入到名字空間中,這就是為什麼我們可以直接訪問它們卻不需要引用它們所來源的模塊。在使用globals函數時,會真切地看到這一切的發生。
在Python程序中,使用內置函數locals()和globals()是不同的。其中locals是只讀的,而globals則不是只讀的。例如在下面的實例代碼中,演示了locals與globals之間的區別。

def func1 (i,info) : #定義函數func1 ()
x = 41 #定義x的初始值
print (locals()) #訪問局部命名空間
locals()["A國"] = 23 #locals是只讀的
print ("A國=",x)
y=55 #定義y的初始值
func1 (1,"first")
globals() ["B國"]= 34 #globals不是只讀的
print("B國=",y)

執行後會輸出:

在Python程序中,locals 實際上沒有返回局部名字空間,它返回的是一個拷貝。 所以對它進行改變對局部名字空間中的變量值並無影響。而globals返回實際的全局名字空間,而不是一個拷貝。所以對globals 所返回的dictionary的任何改動都會直接影響到全局變量。


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved