最近自分でモジュールを分けて開発することが多くなってきたのですが、import関連でつまづくことがしばしばあったので、まとめておきたいと思います。
と、書いて思いましたが、他の言語でも規模が大きめのコードを書いたことがあまりないので、別にPythonが特別難しいってわけではないかもです。私が弱いだけかもしれません。あ、目から水が
importの基本
import <モジュール名>
でimoprtできます。numpyをimportしたかったらimport numpyです。
import <モジュール名> as <別名>
で別名をつけることができます。import numpy as npとか、import pandas as pdとかをよく見ますね。
import <パッケージ名>.<サブパッケージ、またはモジュール名>
で、サブパッケージやモジュールを読み込むこともできます。
他のフォルダを見ようとするとややこしくなる
さて、ここまでは、名前を指定すれば良いだけなので、何も難しくありません。importするモジュールが
- pip等でインストールしたモジュール
- 同フォルダのモジュール
なら良いのですが、自分で作った別フォルダのモジュールをimportしようとすると途端に難しくなります。(とまと基準)
ちなみに、これまで「モジュールをimportする」と言っていますが、モジュールというのは、Pythonの定義や文が入った、拡張子がpyのファイルのことです。
以下のフォルダ構成を例に考えてみます。a1.pyやsub_a1.pyなどがモジュールです。
$ pwd
/tmp/a
$ tree
.
├── __init__.py
├── a1.py
├── a2.py
├── sub_a1
│ ├── __init__.py
│ └── sub_a1.py
└── sub_a2
├── __init__.py
└── sub_a2.py
子フォルダのモジュールをimport
a1.pyからsub_a1/sub_a1.pyをimportするときは、
import sub_a1.sub_a1
でいけます。そんなに難しくはないですね。
親フォルダのモジュールをimport
sub_a1/sub_a1.pyからa1.pyをimportするときは、相対インポートの「..」を使って
from .. import a1
だけではimportできません。
なぜだ…と唸っていたのですが、どうやら親フォルダ「/tmp/a」にもパスが通っていないといけないようです。モジュールをimportするときのパスはsys.pathで確認できます。確認してみたら、確かにa1.pyが見える状態ではありませんでした。つまり、/tmp/aがなかったのです。
ちなみに、カレントディレクトリはsys.pathに入るようで、/tmp/a/sub_a1はありました。
一応解決策はあります。ないなら追加しましょう。sys.pathに親フォルダを追加すれば良いのです。
import sys
sys.path.append("/tmp/a") # 「__file__/..」のようにしても可
import a1 # /tmp/a/a1.pyを読み込む
それか、コードはそのままで、python3 -mでモジュールとして指定すればimportできます。ただし、カレントディレクトリは読み込みたいモジュールの上の階層でないといけません。(この例ではa1.pyのフォルダの1個上の階層)
$ pwd /tmp
$ python3 -m a.sub_a1.sub_a1
実行場所が限定されるのは、良くない気がしますね。
子フォルダから別の子フォルダのモジュールをimport
a1.pyからsub_a1.pyをimportし、そのsub_a1.pyからsub_a2.pyをimportしたいときです。ん、、ややこしいですね。treeで見直すと、以下のような感じです。
$ pwd
/tmp/a
$ tree
.
├── __init__.py
├── a1.py # ①ここから
├── a2.py
├── sub_a1
│ ├── __init__.py
│ └── sub_a1.py # ②これをimportし、ここからさらに
└── sub_a2
├── __init__.py
└── sub_a2.py # ③これをimport
a1.pyから2つimportするのではありません。sub_a2.pyをimportするのはsub_a1.pyです。
さっきの例からして、③は難しそうですね。「またsys.pathをいじらないといけないのかな」と思っていたら、そうでもないようです。
a1.pyには
import sub_a1.sub_a1
と書き、sub_a1.pyには
import sub_a2.sub_a2
と書き、a1.pyを実行するとimportできてしまいます。a1.py -> sub_a1.py -> sub_a2.py、オールOKです。sub_a1.pyのimportコードを見たとき、sub_a1フォルダから(親フォルダをすっとばして)sub_a2を見ているので、違和感を感じますが、できるんです。理由は簡単で、a1.pyを実行したときのsys.pathがsub_a1.pyを読み込んだ時にも引き継がれているためです。つまり、import元(a1.py)の実行時点のsys.pathからimport先(sub_a2.sub_a2)が見えているので、importできるのです。
ただ、個人的には、このやり方はおすすめできません。(こういうの、一般的にはどうなんでしょうか。アリなんでしょうか。)
なぜなら、sub_a1を単体で実行しようとすると
$ python3 sub_a1/sub_a1.py
Traceback (most recent call last):
File "sub_a1/sub_a1.py", line 1, in <module> import sub_a2.sub_a2 ModuleNotFoundError: No module named 'sub_a2'
と、悲しい感じになるためです。「特定のファイル(ここではa1.py)からだけ読み込むならいいのでは?」と思うかもしれませんが、unittestモジュールなどでテストをしようとした際に苦労します。(苦労しました)
ただ、あくまで個人で書くような、「動けばよい」的なものであればそんなに気にしなくて良いと思いますけどね。
結論
親フォルダのモジュールはimportしようとしてはいけない。子フォルダのみimportすべき。
反論は認めます。