ホームページ  >  記事  >  バックエンド開発  >  Python で循環インポートを修正するさまざまな方法

Python で循環インポートを修正するさまざまな方法

Linda Hamilton
Linda Hamiltonオリジナル
2024-11-05 02:21:01729ブラウズ

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 で循環インポートを削除するにはさまざまな方法があります。

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ライブラリの使用

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

Python パッケージの循環インポート

通常、循環インポート 同じパッケージ内のモジュールから取得されます。複雑なプロジェクトでは、パッケージ内にパッケージが存在するため、ディレクトリ構造も複雑になります。

これらのパッケージとサブパッケージには、モジュールへのアクセスを容易にする __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 ファイルを無視して次のようになります。

Different Ways to Fix Circular Imports in Python

メイン ファイルが module_2 に依存し、 module_1 も module_2 に依存し、 module_2 が module_1_1 に依存していることがわかります。インポートサイクルはありません。

しかし、モジュールは __init__.py ファイルに依存しているため、__init__.py ファイルが最初に初期化され、モジュールが再インポートされます。

Different Ways to Fix Circular Imports in Python

現在のインポート サイクルは次のようになります。

Different Ways to Fix Circular Imports in Python

これにより、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 サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。