告別惱人的Python Import

Posted by MingLun Allen Wu on Wednesday, August 18, 2021

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:

  1. module_a.py
  2. module_b.py
  3. 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 指的是

系統路徑中嘗試讀取 PackageModule

系統路徑是一連串的路徑,在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 ModulePackage 時會依序檢查 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



See Also

監控資料品質的旅程