HOWTO · Python

在 Python 中從完整檔案路徑匯入模組

本文將向你展示如何從路徑匯入 Python 檔案。

本頁內容

當外掛、產生的檔案或獨立指令碼位於一般套件結構之外時,從指定檔案路徑匯入模組就很有用。若只需匯入一次,importlib.util 會建立模組物件,再由其載入器於該模組的命名空間中執行檔案。執行之前,請先將模組登錄到 sys.modules,如此一來,初始化期間會查詢模組登錄表的程式碼才能找到該模組本身。

使用 importlib.util 依檔案路徑匯入 Python 檔案

請為檔案指定有效的 Python 模組名稱和確切路徑。模組名稱是模組物件使用的識別字,不必與檔名相同,但應足以在應用程式中保持唯一。以下範例會建立暫存 Python 檔案,因此不受目前工作目錄影響,也能獨立執行。

執行此範例:

本節中的程式碼以及顯示的標籤或輸出仍為英文。

from pathlib import Path
import importlib.util
import sys
import tempfile

with tempfile.TemporaryDirectory() as directory:
    module_path = Path(directory) / "greeting.py"
    module_path.write_text("def message():\n    return 'loaded from path'\n", encoding="utf-8")
    module_name = "greeting_from_path"
    spec = importlib.util.spec_from_file_location(module_name, module_path)
    module = importlib.util.module_from_spec(spec)
    sys.modules[module_name] = module
    spec.loader.exec_module(module)
    print(module.message())

最後一行會印出載入檔案中函式的回傳值。spec_from_file_location() 會建立描述模組名稱與位置的模組規格。module_from_spec() 依據該規格建立模組,而 exec_module() 則透過載入器執行其原始碼。若路徑是動態提供的,使用此模式前請確認模組規格和載入器都可用;若檔案不存在或載入器不支援,就無法用這種方式匯入。

將檔案所在目錄加入 sys.path

若要從某個目錄進行一般匯入,請將檔案所在目錄加入 sys.path,再用模組名稱匯入。sys.path 是 Python 搜尋匯入項目的位置清單。以下獨立範例會在暫存目錄中建立模組、將該目錄加入搜尋路徑,再以名稱匯入模組:

執行此範例:

本節中的程式碼以及顯示的標籤或輸出仍為英文。

from pathlib import Path
import importlib
import sys
import tempfile

with tempfile.TemporaryDirectory() as directory:
    module_name = f"greeting_{Path(directory).name}"
    module_path = Path(directory) / f"{module_name}.py"
    module_path.write_text("def message():\n    return 'loaded by name'\n", encoding="utf-8")
    sys.path.insert(0, directory)
    try:
        module = importlib.import_module(module_name)
        print(module.message())
    finally:
        sys.path.remove(directory)
        sys.modules.pop(module_name, None)

附加或插入目錄會改變目前程序後續的模組搜尋行為,因此請盡量只在需要時修改搜尋路徑,並避免加入由不受信任使用者控制的目錄。範例最後會移除加入的路徑項目和快取模組,以限制影響範圍。如果你有意使用一般匯入語法,或需要從同一目錄匯入多個模組,這種方式會很方便。不過,它仍遵循一般的名稱解析與快取規則:sys.modules 中若已經有同名模組,可能會直接重用;若名稱衝突,也可能載入非預期的檔案。

只有在執行受信任的指令碼文字時才使用 exec()

exec() 會在指定的命名空間中執行 Python 原始碼文字,但不會建立並登錄一般的模組物件。只有在任務是執行受信任的指令碼內容時才使用它,不要以此取代模組匯入。執行來自上傳檔案、請求或其他不受信任來源的文字,可能會以目前程序的權限執行任意 Python 程式碼。你可以用獨立字典保存指令碼建立的名稱,但它不具備真正模組的完整匯入行為或模組身分。

處理模組名稱、套件與匯入錯誤

簡單模組名稱請使用有效的識別字;若要載入多個檔案,請選擇穩定且不重複的名稱。重複使用相同名稱會影響 sys.modules:以該名稱載入其他檔案可能會取代登錄項目,而之後的一般匯入則可能回傳已快取的模組。請先決定重複呼叫時要重用模組,還是刻意重新載入。

直接依路徑載入的檔案不會自動成為其套件的一部分。使用 from .helpers import value 等相對匯入的程式碼仰賴套件脈絡,而單一檔案的模組規格本身無法重建這種脈絡。若要載入套件,建議使用套件名稱匯入,並將其上層目錄加入 sys.path。載入失敗時,請確認路徑指向可讀取的 Python 檔案、模組規格已建立且載入器存在;接著檢查執行模組時的追蹤資訊,找出根本原因。匯入時執行的程式碼本身也可能失敗,因此路徑有效不代表初始化一定成功。

選擇合適的方法

若只需從指定檔案路徑匯入一次,且需要模組物件,請使用 importlib.util。若某個目錄應納入一般匯入搜尋,尤其是要匯入多個相關模組時,請將該目錄加入 sys.path。只有在你要把受信任的原始碼文字當作指令碼執行、而不需要模組語意時,才選擇 exec()。對於可重複使用的專案程式碼,通常以一般套件搭配正常匯入,會更容易管理相依項目、命名和快取。