Python基础 F-03 函数-命名空间与作用域
函數-命名空間與作用域
文章目錄
- 函數-命名空間與作用域
- 一、命名空間
- 1.1、定義與作用
- 1.2、分類
- 1.4、生命周期
- 命名空間創建周期
- 命名空間結束周期
- 二、作用域
- 2.1、定義與作用
- 2.2、分類
- 2.3、查找順序
- 2.4、相關示例
- a、局部訪問局部作用域成功示例
- b、全局訪問正常函數局部作用域失敗示例
- c、全局方法特殊函數的局部作用域失敗示例
- d、全局訪問定義在if、for中的全局作用域成功示例
- e、全局方法導入局部作用域失敗示例
- f、局部訪問全局作用域成功示例
- g、局部訪問局部&全局作用域失敗示例
- h、作用域的靜態特性
- i、作用域類與類方法同級特性
- 三、global 和 nonlocal關鍵字
- 3.1、global關鍵字
- 3.1、enclosing關鍵字
一、命名空間
1.1、定義與作用
我們在編寫Python程序的過程中,如果要使用變量和函數,都需要先對變量和函數命名后才能使用。這個時候,Python就會把命名后的變量和函數分配到不同的命名空間,并通過名稱來識別它們。
命名空間是什么?
- 命名空間(Namespace) 是從名稱到對象的映射,大部分的命名空間都是通過 Python 字典來實現的。
命名空間作用是什么?
- 一是防止命名沖突。比如,在同一.py文件的全局中,不能定義兩個一樣名稱的函數。
- 二是不同的命名空間對應不同的作用域。比如,在兩個.py文件中,可以分別定義都叫func的函數。
1.2、分類
python程序執行期間會有2個或3個活動的命名空間(函數調用時有3個,函數調用結束后2個)。總結有三種命名空間:
- 內置命名空間(built-in namespace), Python 語言內置的名稱,比如函數名 abs、char 和異常名稱 BaseException、Exception 等等。任何模塊均可以訪問。
- 全局命名空間(global namespace),模塊中定義的名稱,記錄了模塊的變量,包括函數、類、其它導入的模塊、模塊級的變量和常量。
- 局部命名空間(local namespace),每個函數所擁有的命名空間,記錄了函數的變量,包括函數的參數和局部定義的變量。(類中定義的也是)
補充說明:
局部命名空間,這里要區分函數以及類定義。函數的局部命名空間,在函數調用時創建,函數返回結果或拋出異常時被銷毀(每一個遞歸函數都擁有自己的命名空間);類定義的命名空間,在解釋器讀到類定義(class關鍵字)時創建,類定義結束后銷毀。
1.4、生命周期
命名空間創建周期
python解釋器啟動 ->創建內建命名空間 -> 加載模塊 -> 創建全局命名空間 ->函數被調用 ->創建局部命名空間
python解釋器啟動 創建內建命名空間 加載模塊 創建全局命名空間 函數被調用 創建局部命名空間命名空間結束周期
函數調用結束 -> 銷毀函數對應的局部命名空間 -> python虛擬機(解釋器)退出 ->銷毀全局命名空間 ->銷毀內建命名空間
函數調用結束 銷毀函數對應的局部命名空間 python虛擬機 解釋器 退出 銷毀全局命名空間 銷毀內建命名空間二、作用域
2.1、定義與作用
作用域是針對命名空間而言,指命名空間在程序里的可應用范圍,或者說是Python程序(文本)的某一段或某幾段,在這些地方,某個命名空間中的名字可以被直接引用。這部分程序就是這個命名空間的作用域。
特別說明:
只有函數、類、模塊會產生新的作用域,代碼塊(例如if、for代碼塊)不會產生新的作用域。
2.2、分類
作用域決定了在哪一部分程序可以訪問哪個特定的變量名稱。Python的作用域一共有4種,分別是:
- L(Local):最內層,包含局部變量,比如一個函數/方法內部。
- E(Enclosing):包含了非局部(non-local)也非全局(non-global)的變量。比如兩個嵌套函數,一個函數(或類) A 里面又包含了一個函數 B ,那么對于 B 中的名稱來說 A 中的作用域就為 nonlocal。
- G(Global):當前腳本的最外層,比如當前模塊的全局變量。
- B(Built-in): 包含了內建的變量/關鍵字等。
2.3、查找順序
在一個 python 程序中,直接訪問一個變量,會從內到外依次訪問所有的作用域直到找到,否則會報未定義的錯誤。
- 規則順序: L –> E –> G –> B。在局部找不到,便會去局部外的局部找(例如閉包),再找不到就會去全局找,再者去內置中找。
- 查找失敗,它將放棄查找并引發一個 NameError 異常,比如:
2.4、相關示例
a、局部訪問局部作用域成功示例
value = '全局的'def print_l():value = '局部的'def print_e():value = '閉包的'print(value)print_e()print(value)print_l() print(value)上述運行結果:
閉包的 局部的 全局的b、全局訪問正常函數局部作用域失敗示例
def print_l():value = '局部的'print_l() print(value)輸出結果:
NameError: name 'value' is not defined分析:print(value)訪問的是全局的,但代碼中只定義了局部的,故訪問失敗,拋了異常
c、全局方法特殊函數的局部作用域失敗示例
list_1 = [i for i in range(5)]print(i)輸出結果:
NameError: name 'i' is not defined分析:lambda、生成器表達式、列表解析式也是函數,都會引入新局部作用域。故全局無法訪問。
d、全局訪問定義在if、for中的全局作用域成功示例
if True:value = '全局的'print(value)for i in range(0, 4):value2 = '全局的'print(value2, i)輸出結果:
全局的 全局的 3分析:if、for代碼塊不會產生新的作用域。故if代碼塊中的value, for代碼塊中的value2,i為全局的,故print能夠訪問到。
e、全局方法導入局部作用域失敗示例
def import_sys():import sysimport_sys()print(sys.path)輸出結果:
NameError: name 'sys' is not defined分析:在函數內部進行模塊導入時,導入的模塊只在函數內部作用域生效。這個算非正常程序員的寫法了,import語句在函數import_sys中將名字sys和對應模塊綁定,那sys這個名字還是定義在局部作用域,跟上面的例子沒有任務區別。要時刻切記Python的名字,對象,這個其他編程語言不一樣。
f、局部訪問全局作用域成功示例
def test():print(i)# 可正常輸出0i = 0 test()在局部作用域中可以引用全局作用域中的命名空間??刹灰J為i=0這行必須寫在def test()前面,事實上只需要在test()函數調用前寫i=0即可,因為函數的命名空間是在函數被調用時創建的。
g、局部訪問局部&全局作用域失敗示例
def test():print(i)i= 2i = 0 test()輸出結果:
UnboundLocalError: local variable 'i' referenced before assignmentPython對局部作用域情有獨鐘,解釋器執行到print(i),i在局部作用域沒有。解釋器嘗試繼續執行后面定義了名字i,解釋器就認為代碼在定義之前就是用了名字,所以拋出了這個異常。
總結:
- 如果局部訪問的變量,在局部沒有定義,按照LEGB原則向上找。一直沒有找到就包NameError異常。
- 如果局部訪問的變量,在局部沒有定義,按照LEGB原則向上找,也找到了,就會使用正常。
- 如果局部訪問的變量,在局部有定義,按照LEGB原則向上找,也找到了。就會拋賦值在引用之前的異常。
h、作用域的靜態特性
i = 1def f1():print(i)def f2():i = 2f1()f2() print(i)輸出結果:
1 1函數的作用域是靜態的,由函數聲明的位置決定,在哪里聲明,就決定了它的上層作用域是誰,這與調用函數的位置無關。無論在哪里調用,它都會去函數本身的作用域中的命名空間找,找不到在去上一層的命名空間找,切記未必是在調用該函數的作用域的命名空間找。
i、作用域類與類方法同級特性
class A(object):a = 2def fun(self):print(a)new_class = A()new_class.fun()輸出結果:
NameError: name 'a' is not defined上文中說過,Python類成員變量與成員函數都有自己的作用域,且各作用域平級。故是平級關系,無法訪問到。需要注意
三、global 和 nonlocal關鍵字
當內部作用域想修改外部作用域的變量時,就要用到global和nonlocal關鍵字了。
3.1、global關鍵字
以下實例修改全局變量 num:
num = 1 def fun1():global num # 需要使用 global 關鍵字聲明print(num) num = 123 # 修改全局的num,局部沒有定義numprint(num) fun1() print(num)輸出結果:
1 123 1233.1、enclosing關鍵字
如果要修改嵌套作用域(enclosing 作用域,外層非全局作用域)中的變量則需要 nonlocal 關鍵字了,如下實例
def outer():num = 10def inner():nonlocal num # nonlocal關鍵字聲明num = 100 # 局部沒定義num,修改的是嵌套的num的值print(num)inner()print(num) # 打印局部的num outer()輸出結果:
100 100總結
以上是生活随笔為你收集整理的Python基础 F-03 函数-命名空间与作用域的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 联客云虚拟工作手机(云手机)应用行业及场
- 下一篇: Composure获取子层级图像:使用变