問題1: 循環インポートによるエラー
同一ディレクトリに以下の2つのPythonファイルが存在する場合:
# file_a.py
from file_b import MyOtherClass
class MyClass:
pass
# file_b.py
from file_a import MyClass
class MyOtherClass:
pass
file_a.pyを実行すると以下のエラーが発生:
Traceback (most recent call last):
File "file_a.py", line 1, in <module>
from file_b import MyOtherClass
File "file_b.py", line 1, in <module>
from file_a import MyClass
File "file_a.py", line 1, in <module>
from file_b import MyOtherClass
ImportError: cannot import name MyOtherClass
インポートメカニズムの詳細
- sys.modulesの確認
- モジュールが存在する場合:対応する__dict__属性から該当クラスを探す
- モジュールが存在しない場合:空のモジュールオブジェクトを作成し、ファイルを実行して__dict__を構築
実行プロセスの追跡
sys.modulesの変化を追跡するコード:
# file_a.py
import sys
try:
print(f"file_bモジュール: {sys.modules['file_b']}")
except KeyError:
print("file_bモジュール未ロード")
try:
print(f"file_aモジュール: {sys.modules['file_a']}")
except KeyError:
print("file_aモジュール未ロード")
from file_b import MyOtherClass
class MyClass:
pass
問題1の原因解明
- file_a.py実行開始時にfile_bモジュールが未ロード
- file_bのインポート処理中にfile_aの再帰的インポートが発生
- file_bモジュールの__dict__が未完成のためMyOtherClassが見つからない
問題2: import文への変更
# file_a.py
import file_b # from文をimport文に変更
class MyClass:
pass
try:
file_b.MyOtherClass()
except AttributeError:
print("file_bにMyOtherClass属性が存在しない")
この変更によりエラーは回避されるが、file_b.MyOtherClass()の呼び出しでAttributeErrorが発生
問題3: 修正方法
インポート順序を変更して解決:
# file_a.py
from file_b import MyOtherClass
class MyClass:
pass
# file_b.py
class MyOtherClass:
pass
from file_a import MyClass # クラス定義後にインポート
※ただし、この記法は非推奨
まとめ
- 循環インポートは避けるべき設計パターン
- モジュールロード時の__dict__生成タイミングに注意
- 開発時以外はreload()の使用を避ける