命名空間在python解釋器中是以字典的形式存在的,是以一種能夠看得見摸得着的實體存在的。做用域是python解釋器定義的一種規則,該規則肯定了運行時變量查找的順序,是一種形而上的虛的規定。html
A namespace is a mapping from names to objects.Most namespaces are currently implemented as Python dictionaries。
命名空間是名字和對象的映射,命名空間是經過 Python Dictionary(字典) 來實現的。python
命名空間提供了一個在大型項目下避免名字衝突的方法面試
Python 中各個命名空間都是獨立的,他們之間無任何關係數據結構
一個命名空間中不能有重名,但不一樣的命名空間是能夠重名而沒有任何影響。閉包
命名空間就像是計算機中的文件夾同樣,同一個文件夾中的文件不可重名,可是若是兩個文件從屬於不一樣的文件夾就能夠重名。app
同理相同的對象名能夠存在不一樣的命名空間中:ide
命名空間的種類分爲 3 類,命名空間的種類也體現了命名空間的生命週期。三個種類及生命週期描述以下:函數
1)內置名稱(built-in names)學習
Python 語言內置的名稱,好比函數名 abs、char 和異常名稱 BaseException、Exception 等等。ui
生命週期:
對於Python built-in names組成的命名空間,它在Python解釋器啓動的時候被建立,在解釋器退出的時候才被刪除;
2)全局名稱(global names)
模塊中定義的名稱,記錄了模塊的變量,包括函數、類、其它導入的模塊、模塊級的變量和常量。
生命週期:
對於一個Python模塊的global namespace,它在這個module被import的時候建立,在解釋器退出的時候退出;
3)局部名稱(local names)
函數中定義的名稱,記錄了函數的變量,包括函數的參數和局部定義的變量。(類中定義的也是)
生命週期:
對於一個函數的local namespace,它在函數每次被調用的時候建立,函數返回的時候被刪除。
注意:命名空間的生命週期取決於對象的做用域,若是對象執行完成,則該命名空間的生命週期就結束。所以,咱們沒法從外部命名空間訪問內部命名空間的對象。例如:
# var1 是全局名稱
var1 = 5
def some_func():
# var2 是局部名稱
var2 = 6
def some_inner_func():
# var3 是內嵌的局部名稱
var3 = 7
命名空間分類圖以下:
若是程序執行時去使用一個變量 hello ,那麼 Python, 查找變量順序爲:
局部的命名空間 -> 全局命名空間 -> 內置命名空間
若是按照這個順序找不到相應的變量,它將放棄查找並拋出一個 NameError 異常:
NameError: name 'hello' is not defined。
python解釋器啓動 ->建立內建命名空間 -> 加載模塊 -> 建立全局命名空間 ->函數被調用 ->建立局部命名空間
函數調用結束 -> 銷燬函數對應的局部命名空間 -> python虛擬機(解釋器)退出 ->銷燬全局命名空間 ->銷燬內建命名空間
一個模塊的引入,函數的調用,類的定義都會引入命名空間,函數中的再定義函數,類中的成員函數定義會在局部namespace中再次引入局部namespace。
A scope is a textual region of a Python program where a namespace is directly accessible. "Directly accessible" here means that an unqualified reference to a name attempts to find the name in the namespace.
做用域就是一個 Python 程序能夠直接訪問命名空間的正文區域。
Python 程序中,直接訪問一個變量,會從內到外依次訪問全部的做用域直到找到,不然會報未定義的錯誤。
Python 中,程序的變量並非在哪一個位置均可以訪問的,訪問權限決定於這個變量是在哪裏賦值的。
Python 中, 變量的做用域決定了在哪一部分程序能夠訪問哪一個特定的變量名稱
做用域分爲4類,分別以下:
L(Local):最內層,包含局部變量,好比一個函數/方法內部。
E(Enclosing):包含了非局部(non-local)也非全局(non-global)的變量。好比兩個嵌套函數,一個函數(或類) A 裏面又包含了一個函數 B ,那麼對於 B 中的名稱來講 A 中的做用域就爲 nonlocal。
G(Global):當前腳本的最外層,好比當前模塊的全局變量。
B(Built-in):包含了內建的變量/關鍵字等,最後被搜索。
做用域規則順序爲:L->E->G->B 若是變量在局部內找不到,便會去局部外的局部找(例如閉包),再找不到就會去全局找,再找不到就去內置中找,以下圖所示:
局部做用域 (Local)是腳本中的最內層,包含局部變量,好比一個函數或方法內部。閉包函數外函數(Enclosing)包含了非局部(non-local)也非全局(non-global)的變量。全局做用域(Global)是當前腳本的最外層,如當前模塊的全局變量,實例以下:
global_scope = 0 # 全局做用域
# 定義閉包函數中的局部做用域
def outer():
o_count = 1 # 閉包函數外的函數中,相對於函數 inner() 來講 做用域非局部
def inner():
local_scope = 2 # 局部做用域
以上實例展現的是全局做用域和閉包函數中的函數,以及函數中的局部做用域,對於函數 inner() 來講,outer() 中的做用域爲 non-local
Python 中的內建做用域(Built-in):包含了內建的變量/關鍵字等,最後被搜索
內建做用域是經過一個名爲 builtin 的標準模塊來實現的,可是這個變量名自身並無放入內置做用域內,因此必須導入這個文件纔可以使用它。在Python3.0中,可使用如下的代碼來查看到底預約義了哪些變量:
import builtinsdir(builtins)['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError'...]
Python 中只有模塊(module),類(class)以及函數(def、lambda)纔會引入新的做用域,其它的代碼塊(如 if/elif/else/、try/except、for/while等)是不會引入新的做用域的,也就是說這些語句內定義的變量,外部也能夠訪問,以下:
name1 = 'SuSan'
if chr('SuSan'.__eq__(name1)):
result = 'I am from China'
else:
result = 'I am from USA'
print(result)
# 輸出結果爲:
I am SuSan,I am from China
實例中 result 變量定義在 if 語句塊中,但外部仍是能夠訪問的。
若是將 result 定義在函數中,則它就是局部變量,外部不能訪問,在代碼中會報錯運行出異常:
# 若是將變量定義在函數內部,則外部不能訪問
def names():
name2 = 'SuSan'
# 在程序調用方法內部的變量報錯
if('SuSan'.__eq__(name2)):
result = 'I am '+name2 +','+'I am from China'
else:
result = 'I am from USA'
print(result)
#運行輸出異常
Traceback (most recent call last):
File "python_scope.py", line 30, in <module>
if('SuSan'.__eq__(name1)):
NameError: name 'name2' is not defined
從以上報錯信息看出,name2 未定義,由於name2 是函數names() 中的局部變量,只能在函數內部調用,外部不能調用函數中的局部變量。
全局變量:定義在函數外部擁有全局做用域的變量
局部變量:定義在函數內部擁有局部做用域的變量
局部變量只能在其被聲明的函數內部訪問,而全局變量能夠在整個程序範圍內訪問。調用函數時,全部在函數內聲明的變量名稱都將被加入到做用域中。以下實例:
# 全局變量和局部變量
total = 0 # 這是一個全局變量
# 函數說明
def sum(arg1, arg2):
# 返回2個參數的和."
total = arg1 + arg2 # total在這裏是局部變量.
print("函數內是局部變量 : ", total)
return total
# 調用sum函數,傳入參數的計算結果顯示局部變量
sum(10, 20)
print("函數外是全局變量 : ", total)
# 輸出結果爲:
函數內是局部變量 : 30
函數外是全局變量 : 0
當內部做用域想修改外部做用域的變量時,就要用到global和nonlocal關鍵字了。
變量訪問順序:
當前做用域局部變量->外層做用域變量->再外層做用域變量->......->當前模塊全局變量->pyhton內置變量
global:全局變量,當局部做用域改變全局變量用global,同時global還能夠定義新的全局變量
nonlocal:外層嵌套函數的變量,nonlocal不能定義新的外層函數變量,只能改變已有的外層函數變量,同時nonlocal不能改變全局變量
num = 1def fun1():# 申明訪問全局變量 global num # 須要使用 global 關鍵字聲明# 輸出全局變量原始值 print(num) # 修改全局變量 num = 123 print(num)# 調用函數fun1()# 輸出修改後的全局變量值print(num)
以上實例輸出結果爲:
1123123
若是要修改嵌套做用域(enclosing 做用域,外層非全局做用域)中的變量則須要 nonlocal 關鍵字
# 定義函數def outer():# 定義變量 num = 10 # 定義嵌套函數 def inner(): nonlocal num # nonlocal關鍵字聲明,使用函數中變量 # 修改變量值 num = 100 print(num) inner() print(num)outer()
以上實例輸出:
100100
另外還有一種特殊狀況,如下這段代碼有語法錯誤,運行會報一個異常:
b = 8
def test():
b = b * 10
print(b)
test()
# 異常信息:UnboundLocalError
程序執行異常:
Traceback (most recent call last): File "python_scope.py", line 90, in <module> test() File "python_scope.py", line 88, in test a = a + 1UnboundLocalError: local variable 'a' referenced before assignment
錯誤信息爲局部做用域引用錯誤,由於 test 函數中的 a 使用的是局部變量,未定義,沒法修改。將 a 修改成全局變量,經過函數參數傳遞,程序就能夠正常執行,輸出結果爲:
b = 8def test(b): b = b * 10 print(b)test(b)
程序輸出結果爲:
80
另外一種解決辦法是加 global 關鍵字:
b = 8def test(): global b b = b * 30 print(b)test()
輸出結果爲:
240
二者的功能不一樣。global關鍵字修飾變量後標識該變量是全局變量,對該變量進行修改就是修改全局變量,而nonlocal關鍵字修飾變量後標識該變量是上一級函數中的局部變量,若是上一級函數中不存在該局部變量,nonlocal位置會發生錯誤(最上層的函數使用nonlocal修飾變量一定會報錯)。
二者使用的範圍不一樣。global關鍵字能夠用在任何地方,包括最上層函數中和嵌套函數中,即便以前未定義該變量,global修飾後也能夠直接使用,而nonlocal關鍵字只能用於嵌套函數中,而且外層函數中定義了相應的局部變量,不然會發生錯誤
本節給你們介紹了 Python 命名空間和做用用戶的介紹與簡單應用,在 Python 開發實戰中對命名空間和做用域的運用比較普遍,謹以此文獻給在 Python 學習道路上的道友,但願對你們有一絲幫助。
參考:
https://www.runoob.com/python3/python3-namespace-scope.html
系列文章