Python - 模組包

chuangzhou發表於2024-04-30

目錄
  • 包匯入示例
  • 包的from 語句 vs 包的 import 語句

包匯入示例

下列三個檔案分別位於目錄dir1 以及 dir1 的子目錄 dir2中,這些檔案的路徑名在註釋中給出:

# dir1\__init__.py
print('dir1 init')
x = 1

# dir1\dir2\__init__.py
print('dir2 init')
y = 2

# dir1\dir2\mod.py
print('in mod.py')
z = 3

這裡,dir1 要麼是我們當前工作所在目錄的(也就是主目錄)的子目錄,要麼就是位於模組搜尋路徑中(實際就是sys.path)的一個目錄的子目錄。無論哪一種,dir1 的容器都不需要_init_.py 檔案。

當Python 向下搜尋路徑的時候,import 語句會在目錄首次遍歷時執行該目錄的初始化檔案。這裡使用print 語句來跟蹤它們的執行:

>>> import dir1.dir2.mod                                              
dir1 init
dir2 init
in mod.py
>>> import dir1.dir2.mod 
>>> 

就像模組檔案一樣,任何已匯入的目錄也可以傳遞給reload,來強制該專案重新執行。就像這裡展示的那樣,reload 可以接受點號路徑名稱來重新載入巢狀的目錄和檔案:

>>> from importlib import reload
>>> reload(dir1) 
dir1 init
<module 'dir1' from 'E:\\PyProject\\dir0\\dir1\\__init__.py'>
>>> reload(dir1.dir2) 
dir2 init
<module 'dir1.dir2' from 'E:\\PyProject\\dir0\\dir1\\dir2\\__init__.py'>
>>> 

一旦匯入後,import 語句內的路徑會變成指令碼中共的一條巢狀物件路徑。在這裡mod 是一個巢狀在dir2 中的子物件,而dir2又巢狀在dir1 中:

>>> dir1
<module 'dir1' from 'E:\\PyProject\\dir0\\dir1\\__init__.py'>
>>> dir2   # 無法直接方法
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'dir2' is not defined. Did you mean: 'dir1'?
>>> dir1.dir2
<module 'dir1.dir2' from 'E:\\PyProject\\dir0\\dir1\\dir2\\__init__.py'>
>>> dir1.dir2.mod
<module 'dir1.dir2.mod' from 'E:\\PyProject\\dir0\\dir1\\dir2\\mod.py'>

實際上,路徑中的每個目錄名稱都會變成賦值了模組物件的變數,而該模組物件的名稱空間則是由該目錄內的_init_.py 檔案中共所有賦值語句進行初始化的。dir1.x 引用了在dir1._init_.py 中賦值的x 變數,mod.z 則引用了在mod.py 內賦值的變數z:

>>> dir1.x
1
>>> dir1.dir2.y 
2
>>> dir1.dir2.mod.z
3
>>> dir1.dir2.mod.y  # 不能訪問y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'dir1.dir2.mod' has no attribute 'y'
>>>

包的from 語句 vs 包的 import 語句

當import 語句和 包一起使用時可能有些不便,因為必須經常在程式中重新輸入路徑。例如,上一節的例子中,每次要得到z時,就得從dir1 開始重新輸入完整得路徑,並且每次都要重新執行整個路徑。如果你想要嘗試直接讀取dir2 或 mod ,就會得到一個錯誤:

>>> dir2.mod
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'dir2' is not defined. Did you mean: 'dir1'?
>>> mod.z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'mod' is not defined

因此,我們可以 讓包使用from語句,來避免每次讀取時都要重新輸入路徑,通常這樣比較方便。也許更重要得是,如果你重新改變目錄結構,那麼from語句只需要在程式程式碼中共更新一次路徑,import 則需要修改很多地方,它提供了一個完整路徑較短得同義詞,並在出現多個同名模組得時候充當重新命名得工具:

>>> from dir1.dir2 import mod                                         
dir1 init
dir2 init
in mod.py
>>> mod.z
3   
>>> import dir1.dir2.mod as mod
>>> mod.z
3   
>>> 

相關文章