TL;DR
開發稍有規模的Python專案時,常會在 import 自定義的模組時遇到狀況,本篇筆記整理使用 absolute import 及 relative import的時機。
Module VS Package
首先我們先定義這兩個名詞 :
Module
Module
指的是任何定義在 *.py
中的 Function
:::python
# module_a.py
def function_a():
print("This is a example function!")
寫在 .py
檔中的 Function 可以被其他Python呼叫:
:::python
# main.py
from module_a import function_a
if __name__ == "__main__":
function_a()
Package
Package
則是將一群 Module
的集合,使用特別的方法將數個 Module
封裝起來:
- 將一至多個
Module
放入相同的資料夾. - 在資料夾中加入一個空白的
__init__.py
假設我們想將三個 Module
封裝成一個 Package
:
- module_a.py
- module_b.py
- module_c.py
根據上面兩個原則,我們需要調整為以下結構:
當上述條件皆滿足,這個資料夾就會被視為一個 Package
,可以被執行檔載入 :
# main.py
from my_package.module_a import xxx
```
---
# Absolute Import VS Relative Import
Python在 Import 檔案時有兩種不同的方式:
## Absolute Import
上述的兩個例子
```python
# main.py
from module_a import function_a # 例子(1)
from my_package.module_a import function_a # 例子(2)
其實都是屬於 Absolute Import
,所謂的 Absolute Import
指的是
從
系統路徑
中嘗試讀取Package
或Module
系統路徑
是一連串的路徑,在Python執行下列指令,會得到一包含許多路徑的 List.
import sys
print(sys.path) # 印出系統路徑
其中值得注意的是: 在執行時,當下的路徑會被自動加入sys.path中
在下列情境中:
如果執行 main.py
是可以運行的:
from module_a import function_a
if __name__ == "__main__":
function_a()
當 main.py
被執行時,會自動將 main.py
當前的資料夾路徑插入 sys.path
(系統路徑)中的第一個位置。
Import Module
或 Package
時會依序檢查 sys.path
的路徑,此時排在 sys.path
首位的路徑就是 main.py
當前的位置(也正是 module_a.py
所在的位置),所以可以正確 import.
然而 Absolute Import
有一個限制 : 名稱必須要是Unique
來個極端的例子,假設現在的 sys.path
長這樣:
["/home/minglun/", “/home/minglun/project”]
如果我們執行下列程式碼:
from module_a import function_a
結果在 /home/minglun
及 /home/minglun/project
兩個位置中都有一個 module_a.py
,那Python怎麼知道要使用哪一個檔案呢?
它無從得知,此時就會發生錯誤!
此時我們就需要 Relative Import
Relative Import
從檔案當前位置為基準,尋找對應的檔案
相同 Package
中的 module
如果需要互相引用,例如 module_a.py
要引用 module_b.py
# module_a.py
from .module_b import function_b # 使用相對路徑指定當前的module_b.py
同一個 Package
中的 Module
如果需要互相引用,使用 Relative Import 可以避免路徑混淆。
幾個重要原則
- 避免在
Module
中放置定義以外的東西:Module
是用來被Import後執行的,執行的程式碼最好放在外部(上述案例中的main.py
),否則容易發生執行上的混亂。 - 同一個
Package
中的Module
互相引用 : 使用Relative Import
- 不同
Package
中的Module
互相引用 : 使用Absolute Import
- 執行檔(
main.py
)引用模組: 使用Absolute Import
只有同一個
Package
中的Module
要互相引用,才用Relative Import
,否則都用Absolute Import