※2019年07月18日:もっと有用な方法を続編の記事にて紹介しています→Pythonの自作モジュールをimportしたいならsys.pathを設定しよう
Pythonは自作のスクリプトでもモジュールとして別ファイルから読み込んで使うことができます。便利ですね。
公式ドキュメント → 6. モジュール (module)
基本的には(?)同階層や下位階層に存在するファイルからモジュールを読み込んで使うようです。
しかし、上位階層にあるモジュールを普通に読み込もうとするとエラーになります。
出力されるエラー内容は Attempted relative import beyond top-level package
です。
スクリプト自身が存在する階層を、最上位のルート階層としてそれぞれ解釈するという挙動をしているのが原因のようですね。
最上位フォルダ
├ 上位フォルダ
│ ├ 下位フォルダ
│ │ └ C.py
│ ├ A.py
│ └ B.py
└ D.py
from B import hoge # 同階層。出来る
from .下位フォルダ.C import foo # 下位階層。出来る
from ..D import bar # `..`は上位階層を示すらしい。なのに出来ない。出来ない。出来ない。出来ない。
しかし、これは各階層がパッケージ化(?)されていれば上位階層でも読み込めるんだそうです。
階層をパッケージ化するための要件はよくわかっていないんですが、とりあえず__init__.py
という名前の空ファイルを全階層に置いておけば良いという理解で私は解決しました。
全階層に__init__.pyを置くことで解決させました。絶対これ間違ってるよね(でも解決したから) pic.twitter.com/lfhaPJYmOm
— あつし⛅ヒト🏃♀️速バラ撒きおじさん (@Anaakikutsushit) July 9, 2019
最上位フォルダ
├ 上位フォルダ
│ ├ 下位フォルダ
│ │ ├ __init__.py # 追加
│ │ └ C.py
│ ├ __init__.py # 追加
│ ├ A.py
│ └ B.py
├ __init__.py # 追加
└ D.py
ウッソだろお前wwwwって目を疑いたくなるような光景ですが、事実これで上手くいったので~~……って書こうとしたらうまく行ってませんでした。とてもつらい。~~いや、やっぱり上手くいきました。う~~ん??????
上手くいったときは、最上位階層でターミナルを開いて、py -m スクリプト.py
の形式で実行しました。この辺り関係あるかも。
とまあ、最後よく分かんなくなっちゃいましたけど、このように__init__.py
さえ置いておけば、先ほどのA.py
の記述のままで上位階層のモジュールも読むことができるようになります。なるみたいなんです。
ホントは同階層か下層に配置してキレイになるようなパッケージの設計をすべきなんでしょうけど、今日のところはとりあえずこれで。