この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは。サービスグループの武田です。
PythonはいわゆるREPL(Read-Eval-Print Loop)と呼ばれる、対話型のモードを持っています。これを使用することで電卓の代わりに計算をしたり、コードスニペットを実行したりできます。
Pythonはimport
命令で他のファイルのコードを読み込むことができますが、これはインタラクティブシェルでも同様です。ところで、ディレクトリ構成と実際に動作させる環境が異なる場合、import
で指定するモジュール名は同一にならない場合があります。
たとえば次のようなディレクトリ構成のプロジェクトがあったとします。
$ tree
.
├── Pipfile
├── Pipfile.lock
└── mod1
└── lib.py
1 directory, 3 files
プロジェクトのルートディレクトリ配下にmod1
があります。ところがデプロイ後はこのmod1
がルートディレクトリになるとします。そうすると、lib.py
を読み込むにはimport lib
と書きますが、このプロジェクト構成では失敗します(import mod1.lib
なら成功します)。簡単に試してみます。
mod1/lib.py
def add(a, b):
return a + b
>>> import lib
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'lib'
モジュール検索パスの確認と追加
Pythonはimport
をする際に探す場所が設定されています。これはsys.path
で確認できますので見てみます。
>>> import sys
>>> print(sys.path)
['', '/usr/local/Cellar/python@3.9/3.9.1_5/Frameworks/Python.framework/Versions/3.9/lib/python39.zip', '/usr/local/Cellar/python@3.9/3.9.1_5/Frameworks/Python.framework/Versions/3.9/lib/python3.9', '/usr/local/Cellar/python@3.9/3.9.1_5/Frameworks/Python.framework/Versions/3.9/lib/python3.9/lib-dynload', '/Users/example/Library/Python/3.9/lib/python/site-packages', '/usr/local/lib/python3.9/site-packages', '/usr/local/Cellar/protobuf/3.14.0/libexec/lib/python3.9/site-packages']
先頭に''
があるためカレントディレクトリのファイルは問題なくimport
できます。ところがlib.py
はmod1
配下のため探し出すことができません。この検索パスに追加することで解決できます。
>>> sys.path.append('mod1')
>>> import lib
>>> lib.add(10, 20)
30
モジュールのリロード
もうひとつ知っておくと便利なTipsとして、モジュールのリロードがあります。開発中でソースコードを修正しながら動作確認をした場合、そのままだと意図した挙動とはなりません。テキストエディタなどを開きlib.py
を変更してみます。
mod1/lib.py
def add(a, b):
return a - b
とりあえず何もせず関数を呼んでみます。
>>> lib.add(10, 20)
30
まぁこれは当然として、一度import
し直してみます。
>>> import lib
>>> lib.add(10, 20)
30
それでも挙動は変わりません。Pythonは一度ロードしたモジュールを何度も読み込まないため、これだけではダメなのです。そこで必要なのがimportlib.reload
です。試してみましょう。
>>> import importlib
>>> importlib.reload(lib)
<module 'lib' from 'mod1/lib.py'>
>>> lib.add(10, 20)
-10
今度は修正後の挙動となりました。
まとめ
モジュールのリロードは、この方法を知るまでは一度シェルを閉じて再起動していました。時間がかかる操作ではありませんが、手間ではあったので、この方法が知れてよかったです。それでは、よいPythonライフを。