在本文中,您將了解命名空間,從名稱到對(duì)象的映射以及變量的作用域。
如果您曾經(jīng)讀過(guò)“ Python之禪(The Zen of Python) ”(在Python解釋器中輸入import this),最后一行指出,命名空間是一個(gè)很棒的主意-讓我們做更多的事情!那么這些神秘的命名空間是什么?首先讓我們看看名稱是什么。
名稱(也稱為標(biāo)識(shí)符)只是賦予對(duì)象的名稱。Python中的一切都是對(duì)象。名稱是訪問(wèn)基礎(chǔ)對(duì)象的一種方式。
例如,當(dāng)我們執(zhí)行賦值操作時(shí)a = 2,2是一個(gè)存儲(chǔ)在內(nèi)存中的對(duì)象,而a是與之關(guān)聯(lián)的名稱。我們可以通過(guò)內(nèi)置函數(shù) 獲取某些對(duì)象的地址(在RAM中)id()。讓我們看看如何使用它。
# 注意:您可能會(huì)得到不同的id值 a = 2 print('id(2) =', id(2)) print('id(a) =', id(a))
輸出結(jié)果
id(2) = 9302208 id(a) = 9302208
在這里,兩者都引用相同的對(duì)象2,因此它們具有相同的id()。讓我們做些有趣的事情。
# 注意:您可能會(huì)得到不同的id值 a = 2 print('id(a) =', id(a)) a = a+1 print('id(a) =', id(a)) print('id(3) =', id(3)) b = 2 print('id(b) =', id(b)) print('id(2) =', id(2))
輸出結(jié)果
id(a) = 9302208 id(a) = 9302240 id(3) = 9302240 id(b) = 9302208 id(2) = 9302208
上述步驟序列中發(fā)生了什么?讓我們用一個(gè)圖來(lái)解釋一下:
最初,創(chuàng)建一個(gè)對(duì)象2并將名稱a與之相關(guān)聯(lián),當(dāng)我們執(zhí)行a = a + 1時(shí),將創(chuàng)建一個(gè)新的對(duì)象3,現(xiàn)在a與該對(duì)象相關(guān)聯(lián)。
請(qǐng)注意,id(a)和id(3)具有相同的值。
此外,當(dāng)執(zhí)行b = 2時(shí),新名稱b與先前的對(duì)象2相關(guān)聯(lián)。
這是有效的,因?yàn)镻ython不必創(chuàng)建新的重復(fù)對(duì)象。 名稱綁定的這種動(dòng)態(tài)特性使Python變得功能強(qiáng)大。 名稱可以引用任何類型的對(duì)象。
>>> a = 5 >>> a = 'Hello World!' >>> a = [1,2,3]
所有這些都是有效的,并且a將在不同示例中引用三種不同類型的對(duì)象。函數(shù)也是對(duì)象,因此名稱也可以引用它們。
def printHello(): print("Hello") a = printHello a()
輸出結(jié)果
Hello
相同的名稱a可以引用一個(gè)函數(shù),我們可以使用該名稱來(lái)調(diào)用該函數(shù)。
現(xiàn)在我們了解了名稱是什么,我們可以繼續(xù)進(jìn)行命名空間的概念。
簡(jiǎn)而言之,命名空間是名稱的集合。
在Python中,您可以將命名空間想象為已定義的每個(gè)名稱到對(duì)應(yīng)對(duì)象的映射。
不同的命名空間可以在給定時(shí)間共存,但完全隔離。
當(dāng)我們啟動(dòng)Python解釋器時(shí),將創(chuàng)建一個(gè)包含所有內(nèi)置名稱的命名空間,并且只要該解釋器運(yùn)行,該命名空間就會(huì)存在。
這就是為什么內(nèi)置的功能(例如id())print()等始終可以從程序的任何部分使用的原因。每個(gè)模塊創(chuàng)建自己的全局命名空間。
這些不同的命名空間是隔離的。因此,不同模塊中可能存在的相同名稱不會(huì)沖突。
模塊可以具有各種功能和類。調(diào)用函數(shù)時(shí)會(huì)創(chuàng)建一個(gè)本地命名空間,其中定義了所有名稱。與類相似。下圖可能有助于闡明這一概念。
盡管定義了各種唯一的命名空間,但我們可能無(wú)法從程序的每個(gè)部分訪問(wèn)它們。作用域的概念開(kāi)始起作用。
作用域是程序的一部分,從那里可以直接訪問(wèn)命名空間而無(wú)需任何前綴。
在任何給定時(shí)刻,至少有三個(gè)嵌套作用域。
具有本地名稱的當(dāng)前函數(shù)的作用域
具有全局名稱的模塊的作用域
具有內(nèi)置名稱的最外部作用域
在函數(shù)內(nèi)部進(jìn)行引用時(shí),將在本地命名空間中搜索名稱,然后在全局命名空間中搜索,最后在內(nèi)置命名空間中搜索。
如果另一個(gè)函數(shù)內(nèi)有一個(gè)函數(shù),則新作用域嵌套在本地作用域內(nèi)。
def outer_function(): b = 20 def inner_func(): c = 30 a = 10
在這里,變量a在全局命名空間中。變量b在的本地命名空間中,outer_function()而c在的嵌套本地命名空間中inner_function()。
當(dāng)我們?cè)跁r(shí)inner_function(),c在我們本地,b在非本地,a在全局。我們可以為c讀取和分配新值,但只能 從中讀取b和ainner_function()。
如果我們嘗試分配作為值b,一個(gè)新的變量b在本地命名空間比非本地不同的創(chuàng)建b。當(dāng)我們分配一個(gè)值,同樣的事情發(fā)生一個(gè)。
但是,如果我們將a聲明為全局a,則所有引用和賦值都將移至全局a。同樣,如果我們想重新綁定變量b,則必須將其聲明為非本地變量。以下示例將進(jìn)一步闡明這一點(diǎn)。
def outer_function(): a = 20 def inner_function(): a = 30 print('a =', a) inner_function() print('a =', a) a = 10 outer_function() print('a =', a)
正如您看到的,該程序的輸出為
a = 30 a = 20 a = 10
在此程序中,在不同的命名空間中定義了三個(gè)不同的變量a并進(jìn)行了相應(yīng)的訪問(wèn)。在以下程序中,
def outer_function(): global a a = 20 def inner_function(): global a a = 30 print('a =', a) inner_function() print('a =', a) a = 10 outer_function() print('a =', a)
程序的輸出是。
a = 30 a = 30 a = 30
在這里,由于使用了關(guān)鍵字global,所以所有引用和賦值都指向全局a。