やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Pythonで別の親ディレクトリに存在するmoduleをimportする

相対パスでは一つ上のディレクトリだけしか対象にできない。

成果物

GitHubPython.import.201709091150

問題

下記ディレクトリ構成のとき、test.pyからmypackage.mymodule.mymethod()を参照せよ。

src/
    tests/
        test.py
    mypackage/
        mymodule.py
            def mymethod()

解法

  • 実行するとき.pyのファイルパスは絶対パスにすること
    • $ python /tmp/Python.import.201709091150/src/tests/test.py
  • sys.path(list型)に参照したいディレクトリの絶対パスを追加するコードを書く
    • その後import mymoduleのようにimport文を書く

実行時のパスがtests/だとエラーになる謎。

カレントディレクト 実行コマンド 結果
/tmp $ python3 /tmp/Python.import.201709091150/src/tests/test.py OK!
/tmp/Python.import.201709091150/src/tests/ $ python3 test.py ImportError: No module named 'mymodule'

成功

$ python3 /tmp/Python.import.201709091150/src/tests/test.py
/tmp/Python.import.201709091150/src/tests
/tmp/Python.import.201709091150/src/mypackage
/tmp/Python.import.201709091150/src/mypackage/__pycache__
mymodule
mymethod

コード

test.py

#ひとつ上の階層における全ディレクトリをimportディレクトリに追加する
import sys, os
rootdir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
for root, dirs, files in os.walk(rootdir):
    for d in dirs:
        print(os.path.join(root, d))
        sys.path.append(os.path.join(root, d))

import mymodule
#import mypackage.mymodule
mymodule.mymethod()

test.pyのディレクトリからは同様に実行できないのが謎かつ面倒。わざわざ絶対パスを指定せねばならない苦痛。

ちなみに、importは以下のような記法もある。いまいち理解できていない。

  • import mymodule
  • from mypackage import mymodule
  • from . import mypackage.mymodule
  • $ python test.py -m src.mypackage.mymodule

解決できない問題

  • モジュールまでの間にあるパッケージがすべて無視されてしまう

異なるパッケージに同一名のモジュールがあったとき、名前の衝突が起きると思われる。

所感

pythonのimportシステムは理解しづらく使いづらく見づらく書きにくく機能不足。自由なディレクトリ構成で自由に参照することができない。

という認識で正しいのかどうかもわからない。