Python命名空間作用域淺析

      網友投稿 853 2022-05-30

      Python3 命名空間和作用域

      命名空間

      先看看官方文檔的一段話:

      A namespace is a mapping from names to objects.Most namespaces are currently implemented as Python dictionaries。

      命名空間(Namespace)是從名稱到對象的映射,像是一個dict,key是變量的名字,value是變量的值。大部分的命名空間都是通過 Python 字典來實現的

      (__dict__)。

      命名空間提供了在項目中避免名字沖突的一種方法。各個命名空間是獨立的,沒有任何關系的,所以一個命名空間中不能有重名,但不同的命名空間是可以重名而沒有任何影響。

      我們舉一個計算機系統中的例子,一個文件夾(目錄)中可以包含多個文件夾,每個文件夾中不能有相同的文件名,但不同文件夾中的文件可以重名。

      一般有三種命名空間:

      內置名稱(built-in names), Python 語言內置的名稱,比如函數名 abs、char 和異常名稱 BaseException、Exception 等等。

      全局名稱(global names),模塊中定義的名稱,記錄了模塊的變量,包括函數、類、其它導入的模塊、模塊級的變量和常量。

      局部名稱(local names),函數中定義的名稱,記錄了函數的變量,包括函數的參數和局部定義的變量。(類中定義的也是)

      命名空間查找順序:

      假設我們要使用變量 runoob,則 Python 的查找順序為:局部的命名空間去 -> 全局命名空間 -> 內置命名空間。

      如果找不到變量 runoob,它將放棄查找并引發一個 NameError 異常:

      NameError: name 'runoob' is not defined。

      對于閉包來說,這里有一點區別,如果在local namespace中找不到變量的話,還會去父函數的local namespace中找變量。

      命名空間的生命周期:

      命名空間的生命周期取決于對象的作用域,如果對象執行完成,則該命名空間的生命周期就結束。

      因此,我們無法從外部命名空間訪問內部命名空間的對象。

      Python命名空間和作用域淺析

      我們可以使用locals() globals()? ?__dict__訪問命名空間【locals()和globals()有一個區別是,locals只讀,globals可以寫】

      import sys m =sys.modules[__name__] #sys.modules獲取了當前運行的模塊 def f(x=1,y=2): m=3 print(locals()) #獲取當前的命名空間(函數體內即為函數內部的命名空間) return 0 f() print(m.__dict__) #__dict__成員獲取了當前模塊的命名空間 print(locals()) #獲取當前的命名空間(全局) print(globals()) #獲取全局的命名空間

      打印結果

      {'x': 1, 'y': 2, 'm': 3} {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001A2B0FA4188>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'D:/flask-vuejs-madblog-master/study/100day/1.py', '__cached__': None, 'time': , 'datetime': , 'sys': , 'm': , 'f': } {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001A2B0FA4188>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'D:/flask-vuejs-madblog-master/study/100day/1.py', '__cached__': None, 'time': , 'datetime': , 'sys': , 'm': , 'f': } {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001A2B0FA4188>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'D:/flask-vuejs-madblog-master/study/100day/1.py', '__cached__': None, 'time': , 'datetime': , 'sys': , 'm': , 'f': }

      【解釋】:遇到一個名字的時候,Python解釋器首先會去本地命名空間(Local)中查找,然后再去其所在函數的作用域(Enclosing Function)中查找,如果還沒找到,就去全局命名空間(Global)中查找,最后會去__builtin__這個模塊中查找,__builtin__模塊在 Python3 中已經重命名成了builtins

      【附】

      from module import 和 import module

      使用import module時,module本身被引入,但是保存它原有的命名空間,所以我們需要使用module.name這種方式訪問它的 函數和變量。

      from module import這種方式,是將其它模塊的函數或者變量引到當前的命名空間中,所以就不需要使用module.name這種方式訪問其它的模塊的方法了。

      if __name__ trick

      python中的module也是對象,所有的modules都有一個內置的屬性__name__,模塊的__name__屬性的值取決于如何使用這個模塊,如果import module,那么__name__屬性的值是模塊的名字。如果直接執行這個模塊的話,那么__name__屬性的值就是默認值__main__。

      module的一些內置屬性

      __name__: 上面已經介紹過

      __file__ : 當前module的絕對路徑

      __dict__:

      __doc__ :

      __package__:

      __path__:

      作用域

      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 –>gt; B。

      在局部找不到,便會去局部外的局部找(例如閉包),再找不到就會去全局找,再者去內置中找。

      實戰分析:

      x = 1234 def test(): print(x) x = 'abc' test()

      在上面的函數中,我們要打印一個名字x,它首先會去本地命名空間中查找,沒有找到。然后去當前函數test的作用域中查找,找到了。此時Python解釋器就會發現x這個名字還沒有添加到local本地命名空間中,就被引用了。所以就會拋出一個異常,說x還未被賦值就被引用了。

      import dis x = 1234 def test_right(): print(x) dis.dis(test_right) print('-' * 20) x = 1234 def test_error(): print(x) x = 'abc' dis.dis(test_error)

      5 0 LOAD_GLOBAL 0 (print) 3 LOAD_GLOBAL 1 (x) 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 9 POP_TOP 10 LOAD_CONST 0 (None) 13 RETURN_VALUE -------------------- 13 0 LOAD_GLOBAL 0 (print) 3 LOAD_FAST 0 (x) 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 9 POP_TOP 14 10 LOAD_CONST 1 ('abc') 13 STORE_FAST 0 (x) 16 LOAD_CONST 0 (None) 19 RETURN_VALUE

      通過查看這兩個函數的反匯編出來的代碼可以看到,這兩個函數訪問x的時候,一個是通過LOAD_GLOBAL指令,訪問的全局命名空間,另外一個則是通過LOAD_FAST指令訪問的本地命名空間。

      Python

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:Promise源碼解密-Promise A+標準
      下一篇:2021年大數據Spark(二十七):SparkSQL案例一花式查詢和案例二WordCount
      相關文章
      亚洲av永久中文无码精品| 亚洲中文无码亚洲人成影院| 国产AV无码专区亚洲AV麻豆丫| 亚洲国色天香视频| 亚洲一本综合久久| 久久亚洲精品中文字幕三区| 久久影视综合亚洲| 亚洲中文字幕日产乱码高清app| 亚洲综合色区在线观看| 久久久久亚洲精品天堂久久久久久| 亚洲精品无码久久不卡| 亚洲一区二区三区在线播放| 亚洲精品专区在线观看| 国产精品亚洲mnbav网站 | 免费亚洲视频在线观看| 亚洲AV无码一区二区一二区| 亚洲av成人一区二区三区观看在线| 色婷婷六月亚洲综合香蕉| 日韩在线视精品在亚洲| 国产精品亚洲一区二区无码 | 黑人粗长大战亚洲女2021国产精品成人免费视频 | 亚洲愉拍99热成人精品热久久| 亚洲日韩精品一区二区三区无码 | 久久久久久久尹人综合网亚洲| 亚洲国产成人片在线观看无码| 亚洲福利视频一区| 亚洲欧洲日产国码在线观看| 亚洲av无码专区在线| 亚洲国产精品成人午夜在线观看| 国产精品亚洲综合网站| 国产成人亚洲精品影院| 亚洲av无码成人黄网站在线观看| 亚洲精品国产成人99久久| 亚洲欧洲日产国码二区首页| 亚洲午夜一区二区三区| 色婷婷六月亚洲综合香蕉| 国产亚洲精品国看不卡| 亚洲av无码成h人动漫无遮挡 | 亚洲国产高清在线精品一区| 亚洲色精品三区二区一区| 亚洲成a人片在线观看久|