Pythonで別ディレクトリのモジュールをインポートしようとすると、ImportErrorやModuleNotFoundErrorが吐き出されることがあります。
このエラーはインポートしようとしたモジュールが、検索対象のパス(sys.path)の中に含まれていないことが原因です。
これを解決する方法を3つ書きます。
デモ環境
ディレクトリ構造は以下のように想定し、app/app.pyからlib/module.pyを読み込もうと思います。
$ pwd
/home/test/
$ tree
.
├── app
│ └── app.py
└── lib
└── module.py
sys.path.append()
app/app.pyでsys.path.append()でsys.pathに任意のディレクトリを追加して、そのディレクトリをモジュール探索対象にします。
import sys
sys.path.append('../lib')
from module import hello
hello()
しかし、この方法はPythonのコーディング規約であるPEP8に違反しています。
$ pycodestyle app/app.py
app/app.py:3:1: E402 module level import not at top of file
出力されたメッセージによると、モジュールをインポートするためのコードはファイルの上部になければならない、ということらしいです。
別のディレクトリにあるモジュールをインポートする前にsys.path.append('../lib')
を実行しているのが原因です。
PEP8に準拠したい場合はこの方法は推奨しません。⬅というか、PEP8に準拠すべきです。
PYTHONPATH
PYTHONPATHという環境変数に自作モジュールのフルパスを代入します。
export PYTHONPATH="$PYTHONPATH:/home/test/lib"
これでsys.pathに追加されます。
しかし、一時的にsys.pathに追加されているだけなので、シェルの別タブや新規シェルではsys.pathには含まれません。
永続的に有効にしたい場合は次のようにします。
echo 'export PYTHONPATH="$PYTHONPATH:/home/test/lib"' >> ~/.bashrc
拡張子pthファイル
拡張子がpthであるファイルをsys.pathに含まれているディレクトリ下に置きます。ファイルの名前は任意です。
ファイルの中には自作モジュールのフルパスを一行に一つ書きます。
デモ環境では、sys.pathに~/.local/lib/python3.6/site-packages
が含まれていると仮定します。
echo "/home/test/lib" > ~/.local/lib/python3.6/site-packages/mymodules.pth
これだけで、ファイルに書いたパスがsys.pathに追加されます。
ちなみに、拡張子pthでは#
でコメントアウトできます。
3つの解決方法を書きましたが、管理のしやすさから拡張子pthのファイルを使用する方法が、個人的にはいいと思っています。