Python で 循環インポート を見たことがありますか?まあ、これはデザインや構造に何か問題があることを示す非常に一般的なコードの匂いです。
循環インポートはどのように行われますか? このインポート エラーは通常、相互に依存する 2 つ以上のモジュールが完全に初期化される前にインポートしようとしたときに発生します。
module_1.py と module_2.py という 2 つのモジュールがあるとします。
# module_1.py from module_2 import ModY class ModX: mody_obj = ModY()
# module_2.py from module_1 import ModX class ModY: modx_obj = ModX()
上記のコード スニペットでは、module_1 と module_2 は両方とも相互に依存しています。
module_1 の mody_obj の初期化は module_2 に依存し、 module_2 の modx_obj の初期化は module_1 に依存します。
これは循環依存関係と呼ばれるものです。両方のモジュールが相互にロードしようとすると、インポート ループでスタックします。
module_1.py を実行すると、次のトレースバックが得られます。
Traceback (most recent call last): File "module_1.py", line 1, in <module> from module_2 import ModY File "module_2.py", line 1, in <module> from module_1 import ModX File "module_1.py", line 1, in <module> from module_2 import ModY ImportError: cannot import name 'ModY' from partially initialized module 'module_2' (most likely due to a circular import)
このエラーは、循環インポートの状況を説明しています。プログラムが module_2 から ModY をインポートしようとしたとき、その時点では module_2 は完全に初期化されていませんでした (moddX を module_1 からインポートしようとする別のインポート ステートメントのため)。
Python で循環インポートを修正するにはどうすればよいですか? Python で循環インポートを削除するにはさまざまな方法があります。
インポートエラーを回避するためにコードを共通ファイルに移動し、そのファイルからモジュールをインポートしてみることができます。
# main.py ----> common file class ModX: pass class ModY: pass
上記のコード スニペットでは、ModX クラスと ModY クラスを共通ファイル (main.py) に移動しました。
# module_1.py from main import ModY class Mod_X: mody_obj = ModY()
# module_2.py from main import ModX class Mod_Y: modx_obj = ModX()
ここで、module_1 と module_2 が main からクラスをインポートすることで、循環インポートの状況が修正されます。
このアプローチには問題があり、コードベースが非常に大きくなり、コードを別のファイルに移動するのが危険になる場合があります。
モジュールの最後に import ステートメントを移動できます。これにより、別のモジュールをインポートする前にモジュールを完全に初期化する時間が与えられます。
# module_1.py class ModX: pass from module_2 import ModY class Mod_X: mody_obj = ModY()
# module_2.py class ModY: pass from module_1 import ModX
クラスまたは関数のスコープ内でモジュールをインポートすると、循環インポートを回避できます。これにより、クラスまたは関数が呼び出された場合にのみモジュールをインポートできるようになります。これは、メモリの使用を最小限に抑えたい場合に関係します。
# module_1.py class ModX: pass class Mod_X: from module_2 import ModY mody_obj = ModY()
# module_2.py class ModY: pass class Mod_Y: from module_1 import ModX modx_obj = ModX()
インポート ステートメントを、それぞれ module_1 と module_2 のクラス Mod_X と Mod_Y スコープ内に移動しました。
module_1 または module_2 を実行すると、循環インポート エラーは発生しません。ただし、このアプローチでは、クラスのスコープ内でのみクラスにアクセスできるようになるため、グローバルにインポートを利用することはできません。
このようにモジュール名または単なるエイリアスを使用すると、問題が解決します。これにより、実行時まで循環依存関係を延期することで、両方のモジュールを完全にロードできるようになります。
# module_1.py from module_2 import ModY class ModX: mody_obj = ModY()
# module_2.py from module_1 import ModX class ModY: modx_obj = ModX()
importlib ライブラリを使用してモジュールを動的にインポートすることもできます。
Traceback (most recent call last): File "module_1.py", line 1, in <module> from module_2 import ModY File "module_2.py", line 1, in <module> from module_1 import ModX File "module_1.py", line 1, in <module> from module_2 import ModY ImportError: cannot import name 'ModY' from partially initialized module 'module_2' (most likely due to a circular import)
# main.py ----> common file class ModX: pass class ModY: pass
通常、循環インポート 同じパッケージ内のモジュールから取得されます。複雑なプロジェクトでは、パッケージ内にパッケージが存在するため、ディレクトリ構造も複雑になります。
これらのパッケージとサブパッケージには、モジュールへのアクセスを容易にする __init__.py ファイルが含まれています。ここで、モジュール間で意図せず循環依存関係が発生することがあります。
次のディレクトリ構造があります。
# module_1.py from main import ModY class Mod_X: mody_obj = ModY()
パッケージ mainpkg と main.py ファイルがあります。 mainpkg 内には 2 つのサブパッケージ modpkg_x と modpkg_y があります。
modpkg_x および modpkg_y 内の各 Python ファイルは次のようになります。
mainpkg/modpkg_x/__init__.py
# module_2.py from main import ModX class Mod_Y: modx_obj = ModX()
このファイルは、module_1 と module_1_1 から両方のクラス (ModX と ModA) をインポートします。
mainpkg/modpkg_x/module_1.py
# module_1.py class ModX: pass from module_2 import ModY class Mod_X: mody_obj = ModY()
module_1 は module_2 からクラス ModY をインポートします。
mainpkg/modpkg_x/module_1_1.py
# module_2.py class ModY: pass from module_1 import ModX
module_1_1 は何もインポートしません。どのモジュールにも依存しません。
mainpkg/modpkg_y/__init__.py
# module_1.py class ModX: pass class Mod_X: from module_2 import ModY mody_obj = ModY()
このファイルは、module_2 からクラス ModY をインポートします。
mainpkg/modpkg_y/module_2.py
# module_2.py class ModY: pass class Mod_Y: from module_1 import ModX modx_obj = ModX()
module_2 は module_1_1 からクラス ModA をインポートします。
main.py ファイル内には次のコードがあります。
root_dir/main.py
# module_1.py import module_2 as m2 class ModX: def __init__(self): self.mody_obj = m2.ModY()
メイン ファイルは module_2 からクラス ModY をインポートします。このファイルは module_2.
に依存しています。ここでインポート サイクルを視覚化すると、modpkg_x および modpkg_y 内の __init__.py ファイルを無視して次のようになります。
メイン ファイルが module_2 に依存し、 module_1 も module_2 に依存し、 module_2 が module_1_1 に依存していることがわかります。インポートサイクルはありません。
しかし、モジュールは __init__.py ファイルに依存しているため、__init__.py ファイルが最初に初期化され、モジュールが再インポートされます。
現在のインポート サイクルは次のようになります。
これにより、module_1_1 が module_1 に依存するようになりました。これは偽の依存関係です。
この場合は、サブパッケージの __init__.py ファイルを空にし、別の __init__.py ファイルを使用すると、パッケージ レベルでインポートを一元化できます。
# module_1.py from module_2 import ModY class ModX: mody_obj = ModY()
この構造では、mainpkg 内に別のサブパッケージ subpkg を追加しました。
メインpkg/サブpkg/__init__.py
# module_2.py from module_1 import ModX class ModY: modx_obj = ModX()
これにより、内部モジュールを単一のソースからインポートできるようになり、相互インポートの必要性が減ります。
これで、main.py ファイル内の import ステートメントを更新できるようになりました。
root_dir/main.py
Traceback (most recent call last): File "module_1.py", line 1, in <module> from module_2 import ModY File "module_2.py", line 1, in <module> from module_1 import ModX File "module_1.py", line 1, in <module> from module_2 import ModY ImportError: cannot import name 'ModY' from partially initialized module 'module_2' (most likely due to a circular import)
これにより、同じパッケージ内のモジュール間の循環依存関係の問題が解決されます。
Python での循環依存関係またはインポートは コードの匂い であり、コードの深刻な再構築とリファクタリングを示します。
Python での循環依存関係を回避するには、上記の方法のいずれかを試すことができます。
?この記事が気に入ったら、興味があるかもしれない他の記事
✅Flask でのテンプレートの継承と例
✅exec() と eval() の違いと例
✅Python でのグローバル キーワードの使用を理解します。
✅Python の型ヒント: 関数、戻り値、変数。
✅関数定義でスラッシュとアスタリスクが使用される理由。
✅学習率は ML モデルと DL モデルにどのような影響を与えますか?
今回は以上です。
コーディングを続けてください✌✌。
以上がPython で循環インポートを修正するさまざまな方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。