[Python] 不正な名前のモジュールを頑張ってimportする方法

モジュールに不正な名前を付けてしまったけど変えられず、でも別ファイルからimportしたい人に向けて。
2023.02.22

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。サービス部の武田です。

Pythonでは変数名などに付けられる名前として命名規約があります。モジュールやパッケージ名も同様です。ところで、一般的にモジュールはOSのファイル、パッケージはディレクトリがそれぞれ対応します。そのため、Pythonとしては不正な(命名規約から外れた)名前をファイルやディレクトリにつけることも可能です。

命名規約 — pep8-ja 1.0 ドキュメント

その場合、もちろん不正な名前なので別ファイルからimportができません。そのため、通常であればファイル名なりを変更するべきです。ですが、何らかの理由で今すぐ名前を変更できないが、importして使いたいというケースに出会うこともあるでしょう。今回はその方法を紹介します。再度言いますが、まずは名前変更を検討してください。

不正な名前の例

たとえばPythonの予約語はモジュール名として不正です。ここではlambda.pyというファイルを作成してみます。

$ tree
.
├── main.py
└── test
    ├── __init__.py
    └── lambda.py

lambda.pyは関数を定義しているだけです。

test/lambda.py

def hello():
    print("Hello World!")

このファイルを別ファイルからimportしてみます。

main.py

import test.lambda

test.lambda.hello()

実行しようとしてもできません。

File "/tmp/main.py", line 1
    import test.lambda
                ^
SyntaxError: invalid syntax

import_moduleを使う方法

先ほど見たように、予約語などはinvalid syntaxとなってimportできませんでした。そこで文字列として指定する方法に切り替えます。importlib.import_moduleは引数としてimportしたいモジュールを指定し、動的にインポートするための関数です。

main.py

import importlib

mymod = importlib.import_module("test.lambda")

mymod.hello()

この方法であれば、問題なくimportして実行できます。

$ python3 main.py
Hello World!

SourceFileLoaderを使う方法

別の方法として、ロードしたいファイルを直接指定する方法もあります。同じように動的にインポートできますが、手順が先ほどより増えています。

main.py

from importlib import machinery, util
from pathlib import Path

mod_path = Path("test/lambda.py")

loader = machinery.SourceFileLoader(str(mod_path), str(mod_path))
spec = util.spec_from_file_location(str(mod_path), mod_path, loader=loader)
mymod = util.module_from_spec(spec)
spec.loader.exec_module(mymod)

mymod.hello()

この方法でも、問題なくimportして実行できます。

$ python3 main.py
Hello World!

まとめ

最初にも言いましたが、まずは不正な名前を付けないようにするのが先決です。それでも付けてしまい、かつ変えられない事情があり、何とかimportしたいんだ!という状況になってしまった時に、思い出してもらえればと思います。