ひろこま Hack Log

プログラミングや機械学習などの知識を記録・共有します

多重にimportするとModuleNotFoundError: No module named 'xxx' というエラーが出る

経緯

先日、 pythonでとあるモジュールを作ったのですが、そのモジュールを別のコードでインポートしたところ、

ModuleNotFoundError: No module named 'xxx'

というエラーが発生しました。 実は、インポートしたモジュールの中で別のモジュールをインポートしていたのですが、後者のインポートがうまくいっていないようでした。

問題を再現してみた

上述の問題をわかりやすくするため、簡単なコードでエラーを再現してみました。

まずは、次に示す child1.pychild2.py という名前のコードを同一ディレクトリに保存します。

child1.py

import child2

def foo():
    child2.bar()

if __name__ == '__main__':
    foo()

child2.py

def bar():
    print('Hello')

このコードは以下のようにして実行することが出来ます。ここまでは当たり前ですね。

$ python child1.py
Hello

ここまでは何の問題もありません。

では、このchild1.pyを別のコードから呼び出してみようと思います。

呼び出し元のコードはparent.pyとします。

parent.py

from children import child1

child1.foo()

それぞれのソースの保存場所はこのような関係になっているとします。

$ tree .
.
├── children
│   ├── child1.py
│   └── child2.py
└── parent.py

ここまでの状況を整理すると下図のようになります。

f:id:twx:20190326001327p:plain
それぞれのソースの関係性

では、parent.pyを実行してみましょう。

$ python parent.py

Traceback (most recent call last):
  File "parent.py", line 1, in <module>
    from children import child1
  File "/hogehoge/children/child1.py", line 1, in <module>
    import child2
ModuleNotFoundError: No module named 'child2'

エラーが出ました。child2という名前のモジュールが見つからないと言われてしまいまいましたね。

問題の原因

上記のエラーが出た理由は、インポート先として有効なパスの中に child2.py が含まれていなかったからです。

通常、インポート先として有効なパスは

  1. スクリプトファイルがあるディレクトリ
  2. 環境変数PYTHONPATHで指定したディレクトリ
  3. カレントディレクトリ
  4. 標準ライブラリのためのディレクトリ
  5. pipでインストールしたライブラリが入っているディレクトリ

の5種類です。

カレントディレクトリの下の階層を再起的に見に行ってくれるわけではないので、これでは child2.py を参照することはできないのです。

解決策

child1.py の中身を以下のように変更してください。

from children import child2

def foo():
    child2.bar()

if __name__ == '__main__':
    foo()

1行目だけ変更しました。

python.py があるディレクトリの直下に children ディレクトリが存在するので、 python.py があるディレクトリ内で python.py を実行すれば children ディレクトリにはパスが通っているので、その下にある child2.py をインポートすることができます。

ただし、これでは child1.py があるディレクトリ内で child1.py を実行するとエラーが起きます。 children にパスが通っていないためです。

この問題への対策方法としては、環境変数 PYTHONPATHparent.py があるディレクトリパスを設定することが有効です。

$ cd [parent.pyがあるディレクトリ]
$ export PYTHONPATH=$PYTHONPATH:`pwd`

環境変数を設定後、いろいろな場所で parent.pychild1,py を実行してみました。

$ python parent.py
Hello

$ python children/child1.py
Hello

$ cd children
$ python child1.py
Hello

$ python ../parent.py
Hello

いずれも正しく動いています!!

本日は、多重にモジュールをインポートしたときに起きる ModuleNotFoundError: No module named 'xxx' というエラーの原因と対策についてご紹介しました。

良い記事だと思っていただいた方は、以下の「★+」ボタンのクリック、SNSでのシェア、「読者になる」ボタンのクリック、Twitterのフォローをお願いします!

Koma Hirokazu 's Hacklog ―― Copyright © 2018 Koma Hirokazu