Python で .py
拡張子のついていない Python ファイル(モジュール)を import
する方法についてです。
まず先に結論ですが、 .py
拡張子の無い Python ファイルを読み込みたくなったときは、 .py
拡張子をつけてふつうの import
文で読み込む というのがベストかと思います。
何らかの理由で .py
拡張子をつけられない事情があるなら、それができる方法がないかをまずは考えてみるべきです。
考えてもどうしてもダメだった場合のみ以下の方法を検討するとよいと思います。
確認時のバージョン
- Python 3.10
.py
拡張子の無い Python ファイルを import
する方法
Python 本体に同梱のライブラリ importlib
を使うとできます。
次の関数 import_path()
を定義すると
from importlib.machinery import SourceFileLoader
from importlib.util import module_from_spec, spec_from_file_location
from pathlib import Path
def import_path(path: Path):
"""パス指定で Python モジュールを読み込む"""
module_name = path.stem.replace('-', '_')
loader = SourceFileLoader(module_name, str(path))
spec = spec_from_file_location(module_name, path, loader=loader)
module = module_from_spec(spec)
spec.loader.exec_module(module)
return module
次の形で pathlib.Path
インスタンスを使ってモジュールを読み込めます。
mymodule_path = Path(...)
mymodule = import_path(mymodule_path)
試しに私の手元で load_mymodule.py
と mymodule
という 2 つのファイルを作成し同階層に置いて load_mymodule.py
を実行してみました。
cd sample/
tree
.
├── load_mymodule.py
└── mymodule
ファイルの中身はそれぞれ次のとおりです。
sample/load_mymodule.py
:
from importlib.machinery import SourceFileLoader
from importlib.util import module_from_spec, spec_from_file_location
from pathlib import Path
def import_path(path: Path):
"""パス指定で Python モジュールを読み込む"""
module_name = path.stem.replace('-', '_')
loader = SourceFileLoader(module_name, str(path))
spec = spec_from_file_location(module_name, path, loader=loader)
module = module_from_spec(spec)
spec.loader.exec_module(module)
return module
if __name__ == '__main__':
mymodule_path = Path(__file__).parent / 'mymodule'
mymodule = import_path(mymodule_path)
sample/mymodule
:
print(f'{__name__} is loaded.')
load_mymodule.py
を実行すると無事に mymodule
が読み込めることが確認できました。
python load_mymodule.py
# => mymodule is loaded.
以上です。
ただし、この方法が正規に推奨されている方法というわけではないので、参考にされる際はあくまでひとつのやり方として参考にしてください。
ちなみに、 Python 3.4 以前のバージョンでは importlib
ではなく imp
ライブラリを使ったシンプルな方法があったらしく、検索していると imp
を使った方法が紹介されているのをちらほら目にしました。
import imp
def import_path(path: Path):
module_name = path.stem.replace('-', '_')
return imp.load_source(module_name, str(path))
しかし imp
は deprecated となっており Python 3.12 で削除される予定とされています。
DeprecationWarning: the imp module is deprecated in favour of importlib and slated for removal in Python 3.12; see the module's documentation for alternative uses
冒頭のコメントの繰り返しですが、ほとんどの場合は単純に拡張子 .py
をつけて import
文で読み込むというのがおそらく素直でベストなやり方なので、まずはそのやり方が採れないかとよく検討するのがよいかと思います。