国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

Python進階_關于命名空間與作用域(詳解)

2019-11-25 16:08:29
字體:
來源:轉載
供稿:網友

寫在前面

如非特別說明,下文均基于Python3

命名空間與作用于跟名字的綁定相關性很大,可以結合另一篇介紹Python名字、對象及其綁定的文章。

1. 命名空間

1.1 什么是命名空間

Namespace命名空間,也稱名字空間,是從名字到對象的映射。Python中,大部分的命名空間都是由字典來實現的,但是本文的不會涉及命名空間的實現。命名空間的一大作用是避免名字沖突:

def fun1():  i = 1def fun2():  i = 2

同一個模塊中的兩個函數中,兩個同名名字i之間絕沒有任何關系,因為它們分屬于不同明明空間。

1.2 命名空間的種類

常見的命名空間有:

built-in名字集合,包括像abs()這樣的函數,以及內置的異常名字等。通常,使用內置這個詞表示這個命名空間-內置命名空間

模塊全局名字集合,直接定義在模塊中的名字,如類,函數,導入的其他模塊等。通常,使用全局命名空間表示。

函數調用過程中的名字集合,函數中的參數,函數體定義的名字等,在函數調用時被“激活”,構成了一個命名空間。通常,使用局部命名空間表示。

一個對象的屬性集合,也構成了一個命名空間。但通常使用objname.attrname的間接方式訪問屬性,而不是直接訪問,故不將其列入命名空間討論。

類定義的命名空間,通常解釋器進入類定義時,即執行到class ClassName:語句,會新建一個命名空間。(見官方對類定義的說明)

1.3 命名空間的生命周期

不同類型的命名空間有不同的生命周期:

內置命名空間,在Python解釋器啟動時創建,解釋器退出時銷毀;

全局命名空間,模塊的全局命名空間在模塊定義被解釋器讀入時創建,解釋器退出時銷毀;

局部命名空間,這里要區分函數以及類定義。函數的局部命名空間,在函數調用時創建,函數返回或者由未捕獲的異常時銷毀;類定義的命名空間,在解釋器讀到類定義創建,類定義結束后銷毀。(關于類定義的命名空間,在類定義結束后銷毀,但其實類對象就是這個命名空間內容的包裝,見官方對類定義的說明)

2. 作用域

2.1 什么是作用域

作用域是Python的一塊文本區域,這個區域中,命名空間可以被“直接訪問”。這里的直接訪問指的是試圖在命名空間中找到名字的絕對引用(非限定引用)。這里有必要解釋下直接引用和間接引用:

直接引用;直接使用名字訪問的方式,如name,這種方式嘗試在名字空間中搜索名字name。

間接引用;使用形如objname.attrname的方式,即屬性引用,這種方式不會在命名空間中搜索名字attrname,而是搜索名字objname,再訪問其屬性。

2.2 與命名空間的關系

現在,命名空間持有了名字。作用域是Python的一塊文本區域,即一塊代碼區域,需要代碼區域引用名字(訪問變量),那么必然作用域與命名空間之間就有了聯系。

顧名思義,名字作用域就是名字可以影響到的代碼文本區域,命名空間的作用域就是這個命名空間可以影響到的代碼文本區域。那么也存在這樣一個代碼文本區域,多個命名空間可以影響到它。
作用域只是文本區域,其定義是靜態的;而名字空間卻是動態的,只有隨著解釋器的執行,命名空間才會產生。那么,在靜態的作用域中訪問動態命名空間中的名字,造成了作用域使用的動態性。

那么,可以這樣認為:

靜態的作用域,是一個或多個命名空間按照一定規則疊加影響代碼區域;運行時動態的作用域,是按照特定層次組合起來的命名空間。

在一定程度上,可以認為動態的作用域就是命名空間。在后面的表述中,我會把動態的作用域與其對應命名空間等同起來。

2.3 名字搜索規則

在程序中引用了一個名字,Python是怎樣搜索到這個名字呢?

在程序運行時,至少存在三個命名空間可以被直接訪問的作用域:

Local
首先搜索,包含局部名字的最內層(innermost)作用域,如函數/方法/類的內部局部作用域;

Enclosing
根據嵌套層次從內到外搜索,包含非局部(nonlocal)非全局(nonglobal)名字的任意封閉函數的作用域。如兩個嵌套的函數,內層函數的作用域是局部作用域,外層函數作用域就是內層函數的 Enclosing作用域;

Global
倒數第二次被搜索,包含當前模塊全局名字的作用域;

Built-in
最后被搜索,包含內建名字的最外層作用域。

程序運行時,LGB三個作用域是一定存在的,E作用域不一定存在;若程序是這樣的:

i = 1print(i)

局部作用域在哪里呢?我們認為(Python Scopes And Namespaces):

Usually, the local scope references the local names of the (textually) current function. Outside functions, the local scope references the same namespace as the global scope: the module's namespace. Class definitions place yet another namespace in the local scope.

一般地,局部作用域引用函數中定義的名字。函數之外,局部作用域和全局作用域引用同一個命名空間:模塊的明星空間。然而類型的局部作用域引用了類定義新的命名空間。

Python按照以上L-E-G-B的順序依次在四個作用域搜索名字。沒有搜索到時,Python拋出NameError異常。

2.4 何時引入作用域我們知道:

我們知道:

在Python中一個名字只有在定義之后,才能引用。

print(i)

直接引用未定義的名字i,按照搜索規則,在LGB三個作用域均沒有搜索到名字i(LB相同命名空間)。拋出NameError異常:

Traceback (most recent call last): File "scope_test.py", line 15, in <module>  print(i)NameError: name 'i' is not defined

那對于這段代碼呢?

def try_to_define_name():  '''函數中定義了名字i,并綁定了一個整數對象1'''  i = 1try_to_define_name()print(i) #引用名字i之前,調用了函數

在引用名字i之前,明明調用了函數,定義了名字i,可是還是找不到這個名字:

Traceback (most recent call last): File "scope_test.py", line 20, in <module>  print(i) #引用名字i之前,調用了函數NameError: name 'i' is not defined

雖然定義了名字i,但是定義在了函數的局部作用域對應的局部命名空間中,按照LEGB搜索規則,在全局作用域中自然訪問不到局部作用域;再者,函數調用結束后,這個命名空間被銷毀了。

引用名字總是與作用域相關的,因此:

在Python中一個名字只有在定義之后,才能在合適的作用域引用。

那么,在定義名字時,就要注意名字定義的作用域了,以免定義后需要訪問時卻找不到。所以,了解Python在何時會引入新的作用域很有必要。一般來說,B,G兩個作用域的引入在不能夠通過代碼操作的,能夠通過語句引入的作用域只有E,L了。Python中引入新作用域的語句很有限,總的來說只有兩類一個:

函數定義引入local作用域或者Enclosing作用域;本質上,lambda和生成器表達式也是函數,會引入新作用域。

類定義引入local作用域;

列表推導式引入local作用域,傳說在python2中列表推導式不引入新的作用域

幾個會讓有其他高級語言經驗的猿困惑的地方:

if語句:

if True:  i = 1print(i) # output: 1,而不是NameError

if語句并不會引入新的作用域,所以名字綁定語句i = 1與print(i)是在同一個作用域中。

for語句:

for i in range(6):  passprint(i) #output: 5,而不是NameError

for語句同樣不會引入新的作用域,所以名字i的綁定和重綁定與print(i)在同一個作用域。這一點Python就比較坑了,因此寫代碼時切忌for循環名字要與其他名字不重名才行。

import語句:

def import_sys():  '''import sys module'''  import sysimport_sys()print(sys.path) # NameError: name 'sys' is not defined

這個算非正常程序員的寫法了,import語句在函數import_sys中將名字sys和對應模塊綁定,那sys這個名字還是定義在局部作用域,跟上面的例子沒有任務區別。要時刻切記Python的名字,對象,這個其他編程語言不一樣,但是:

打破第一編程語言認知的第二門編程語言,才是值得去學的好語言。

3. 作用域應用

3.1 自由變量可讀不可寫

我不太想用“變量”這個詞形容名字,奈何變量是家喻戶曉了,Python中的自由變量:

If a variable is used in a code block but not defined there, it is a free variable.

如果引用發生的代碼塊不是其定義的地方,它就是一個自由變量。專業一點,就是:

引用名字的作用域中沒有這個名字,那這個名字就是自由名字

Note: “自由名字”只是作者YY的,并沒得到廣泛認可。

我們已經了解了作用域有LEGB的層次,并按順序搜索名字。按照搜索順序,當低層作用域不存在待搜索名字時,引用高層作用域存在的名字,也就是自由名字:

[示例1]

def low_scope():  print(s)s = 'upper scope'low_scope()

很清楚,這段代碼的輸出是upper scope。

[示例2]

def low_scope():  s = 'lower scope's = 'upper scope'low_scope()print(s)

很遺憾,最后的打印語句沒有按照期待打印出lower scope而是打印了upper scope。

A special quirk of Python is that 

主站蜘蛛池模板:
宜昌市|
彩票|
隆子县|
南澳县|
利津县|
称多县|
浦东新区|
滦平县|
太湖县|
河池市|
丰县|
蓝山县|
汶上县|
错那县|
云梦县|
秭归县|
无极县|
常州市|
苍梧县|
特克斯县|
白河县|
云阳县|
虹口区|
五峰|
洞头县|
区。|
三明市|
台州市|
惠水县|
当雄县|
晋中市|
绥芬河市|
营口市|
育儿|
沾益县|
新化县|
手游|
垣曲县|
南京市|
夏津县|
陆川县|