python3.9 のインポート文の使い方について、まとめる。
これでもう悩まない!
(2022/08/27 更新: 動作確認, 循環参照についての言及を追加, その他記事微修正)
同じディレクトリからインポート
同じ階層に module.py があるとき、
import module
from module import func_in_module
from module import *
# import module.func_in_module # エラー
などとできる。
(funcはクラスでも同じ。以下同様)
ひとつ下のディレクトリからインポート
同じ階層にディレクトリ package があり、その中に module2.py があるとき、
import package.module2
from package import module2
from package.module2 import func_in_module2
などできる。
ふたつ以上下のディレクトリからインポート
前項の package ディレクトリの中に subpackage ディレクトリがあり、その中に module3.py があるとき、
import package.subpackage.module3
from package.subpackage import module3
ができる(あまり使い所があるとも思えないが)。
パッケージ化したときの挙動
上述の package ディレクトリ、subpackage ディレクトリに __init__.py という名前のファイル(中身は空で良い)を配置すると、package や subpackage がパッケージ(モジュールをいい感じにまとめたもの)として認識されるようになる。
このとき、package/module2.py においては、
from . import subpackage
from .subpackage import module3
# import subpackage # エラー
# from subpackage import module3 # エラー
# import .subpackage # 以前は使えた?が Python3.8 以降では Syntax error
のように . を先頭に使った位置関係の指定が使えるようになる(というか、使わないとエラーになるので注意)。
subpackage 以下にさらにサブパッケージがある場合は、さらに.でつなげることでインポートできる。
また、例えば package/subpackage/module3.py においては、
from .. import module2
from ..module2 import *
from ..module2 import func_in_module # エラー
# import ..module2 # シンタックスエラー
のように、.. を使って一つ上のディレクトリからインポートすることもできる。
繰り返しだが、これは __init__.py を両方のディレクトリに配置してパッケージ化したときの挙動である。
パッケージ化して外部からimport
前節では __init__.py は空としたが、ここに同じルールでインポート文を書くことで再帰的にインポートできる。これによって外部から最上位のモジュールをインポートするだけでの下位モジュールまでアクセスできるようになる。
例えば、
import .module2 # package/module2.pyというファイルがある
import .subpackage # package/subpackageというディレクトリがある
import .module3 # package/subpackage/module3.pyというファイルがある
としたうえで package ディレクトリに PythonPath を通しておけば、
import package
print(package.module2.function)
print(package.subpackage.module3.function)
のように、package を import するだけでアクセスできるようになる。
愚直にコロンをつなげるのが面倒であれば、例えば
from .subpackage.module3 import function
のように書けば名前が圧縮されて
import package
print(package.function)
のように参照できるようになる。
余談だが、__all__ を用いることで再帰的にインポートするモジュールの指定を簡略に行う方法もある:
Python公式ドキュメント
※ 注意 ※
上記のインポート文たちはいずれかひとつを書くぶんには動くが、複数を同時に書くと次のようにエラーになることがある。
ImportError: cannot import name 'foo' from partially initialized module 'package.module2' (most likely due to a circular import)
これは module.py のインポート文を処理しているあいだに module2.py で module.py をインポートしようとしたために起こるエラー(循環参照, 循環インポート?)である。実務でもしばしば遭遇するので注意。
こうなってしまった場合は、循環参照しないようなモジュール構成を考え直す(インポート関係がDAGになるようにする)か、同しようもない場合は実行時インポートにする(ファイルの先頭に書くのでなく、関数内など実行時に走る部分にインポート文を書く)。
パッケージ化していないときに上下のディレクトリから
パッケージ化してないけどファイルの置いてある一つ上のディレクトリからインポートしたいときは、
import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
import (一つ上のディレクトリにあるpythonファイル名)
とすれば良い。
__file__ でファイルのパスを取得し、その一つ上のディレクトリを動的にモジュール検索対象のパスに加えている。
2行目を
sys.path.append("..")
とするのは、プログラムを実行する場所によって結果が変わってしまうため、不適切。
結局実用上は
少量のコードであれば、困ったら sys.path.append() する。
まとまった量のコーディングをするのであればパッケージ化し、一番上のパッケージをインポートするだけで、中のサブパッケージをインポートするように __init__.py に書く。具体的には、各ディレクトリの __init__.py に
from .(同じ階層に存在するディレクトリ名) import *
をディレクトリの数だけ書く。
これで一番上のディレクトリをパスに追加して
from package import *
するだけでその中で定義した全てのクラスが使えるようになる(* は説明のために書いてるだけで、実際は適宜行儀の良いインポート文を書くのがよい)。