isatoの活動日記

管理人isatoが毎日の生活で気になったこと、勉強になったことを書いています。

Python3 モジュール化 (相対パスに注意)

pythonに関わらず、アプリを開発しているときに独自のモジュールを作ることはよくあると思います。

そこで注意するべき点について紹介します。

ファイル構造をこのようにします。

work/
  |--mymodule.py
  `--call.py

各ファイルを次のように編集します。

def greet():
    print("hello")
from mymodule import greet

def call():
    greet()

if __name__ == "__main__":
    call()

call.pyを実行すると、

$ python3 call.py
hello

となります。

では、さらに上の階層から、call.pyを呼び出す場合を考えましょう。

top/
  |--main.py
  `--work/
        |--mymodule.py
        `--call.py
from work.call import call
call()
$ python3 main.py
Traceback (most recent call last):
  File "main.py", line 1, in <module>
    from work.call import call
  File "~/top/work/call.py", line 1, in <module>
    from mymodule import greet
ModuleNotFoundError: No module named 'mymodule'

このように実行に失敗します。

なぜこうなったか

pythonではプログラムを実行すると、実行されたディレクトリ + sys.pathに保存されたパス の中から名前解決を行おうとします。

一つ目の例では、実行されたディレクトリ(work)内にmymodule.pyがあったため、問題なく実行できました。 しかし、二つ目の例では実行されたディレクトリ(top)内にmymodule.pyがなく、sys.pathにもworkディレクトリが登録されていなかったため、実行できませんでした。

これを動作させるためには、workディレクトリをsys.pathに登録すればよいです。

import sys
sys.path.append("work/")
from work.call import call
call()
$ python3 main.py
hello
これで実行できました。

ただ、このようなファイル構造よりも、次のようにすると使いやすいかもしれません。

    1. 予め独自モジュールを置いておくディレクトリAを作成しておく。
    2. ディレクトリAをsys.pathに登録しておく。
    3. 独自モジュールはディレクトリAに保存するようにする。

以上です。