だから私はこのエラーを取得しています
Traceback (most recent call last):
File "/Users/alex/dev/runswift/utils/sim2014/simulator.py", line 3, in <module>
from world import World
File "/Users/alex/dev/runswift/utils/sim2014/world.py", line 2, in <module>
from entities.field import Field
File "/Users/alex/dev/runswift/utils/sim2014/entities/field.py", line 2, in <module>
from entities.goal import Goal
File "/Users/alex/dev/runswift/utils/sim2014/entities/goal.py", line 2, in <module>
from entities.post import Post
File "/Users/alex/dev/runswift/utils/sim2014/entities/post.py", line 4, in <module>
from physics import PostBody
File "/Users/alex/dev/runswift/utils/sim2014/physics.py", line 21, in <module>
from entities.post import Post
ImportError: cannot import name Post
そして、同じインポートステートメントをさらに上に使用すると動作することがわかりますか?循環インポートに関して書かれていないルールはありますか?同じクラスを呼び出しスタックのさらに下で使用するにはどうすればよいですか?
Jpmc26による答えは、決してwrongではありませんが、循環インポートではあまりにも大きく低下すると思います。正しく設定すれば、問題なく機能します。
これを行う最も簡単な方法は、import my_module
ではなくfrom my_module import some_object
構文を使用することです。前者は、my_module
が含まれているとインポートされる場合でも、ほとんど常に機能します。後者は、my_object
がmy_module
ですでに定義されている場合にのみ機能します。これは、循環インポートでは当てはまらない場合があります。
特定のケースに合わせて:entities/post.py
を変更してimport physics
を実行し、PostBody
だけでなくphysics.PostBody
を直接参照してください。同様に、physics.py
を変更してimport entities.post
を実行し、Post
だけでなくentities.post.Post
を使用します。
モジュール(またはそのメンバー)を初めてインポートすると、モジュール内のコードは他のコードと同様に順番に実行されます。たとえば、関数の本体とまったく異なる扱いはされません。 import
は、他のすべてのコマンド(割り当て、関数呼び出し、def
、class
)と同じです。インポートがスクリプトの上部で行われると仮定すると、次のことが起こります。
World
からworld
をインポートしようとすると、world
スクリプトが実行されます。world
スクリプトはField
をインポートします。これにより、entities.field
スクリプトが実行されます。Post
をインポートしようとしたため、entities.post
スクリプトに到達するまで続きます。entities.post
スクリプトは、physics
をインポートしようとするため、PostBody
モジュールを実行します。physics
はentities.post
からPost
をインポートしようとしますentities.post
モジュールがまだメモリに存在するかどうかはわかりませんが、実際には問題ではありません。モジュールがメモリにないか、モジュールにまだPost
メンバーがありませんPost
を定義するための実行が終了していませんPost
がインポートされるために存在しないため、エラーが発生しますいいえ、それは「コールスタックのさらに上への作業」ではありません。これは、エラーが発生した場所のスタックトレースです。つまり、そのクラスでPost
をインポートしようとしてエラーが発生しました。循環インポートを使用しないでください。せいぜい、無視できる利益(通常、no利益)があり、このような問題を引き起こします。それを維持する開発者に負担をかけ、卵殻を壊さないように強制的に卵の殻の上を歩かせます。モジュール組織をリファクタリングします。
循環依存関係を理解するには、Pythonが本質的にスクリプト言語であることを覚えておく必要があります。メソッド外のステートメントの実行はコンパイル時に発生します。インポート文はメソッド呼び出しのように実行されます。それらを理解するには、メソッド呼び出しのように考える必要があります。
インポートを行うとき、インポートするファイルが既にモジュールテーブルに存在するかどうかによって異なります。存在する場合、Pythonは現在シンボルテーブルにあるものを使用します。そうでない場合、Pythonはモジュールファイルの読み取りを開始し、そこで見つかったものをすべてコンパイル/実行/インポートします。コンパイル時に参照されたシンボルは、それらが見られたか、コンパイラによってまだ見られていないかに応じて、検出されるかどうかが決まります。
次の2つのソースファイルがあるとします。
ファイルX.py
def X1:
return "x1"
from Y import Y2
def X2:
return "x2"
ファイルY.py
def Y1:
return "y1"
from X import X1
def Y2:
return "y2"
ここで、ファイルX.pyをコンパイルするとします。コンパイラは、メソッドX1を定義することから始めて、X.pyのインポートステートメントを見つけます。これにより、コンパイラはX.pyのコンパイルを一時停止し、Y.pyのコンパイルを開始します。その後まもなく、コンパイラはY.pyのimportステートメントをヒットします。 X.pyは既にモジュールテーブルにあるため、Pythonは既存の不完全なX.pyシンボルテーブルを使用して、要求された参照を満たします。 X.pyのimportステートメントの前に表示されるシンボルはすべてシンボルテーブルにありますが、その後のシンボルはそうではありません。 X1はimportステートメントの前に表示されるようになったため、正常にインポートされました。 PythonはY.pyのコンパイルを再開します。そうすることで、Y2を定義し、Y.pyのコンパイルを終了します。次に、X.pyのコンパイルを再開し、Y.pyシンボルテーブルでY2を見つけます。コンパイルは最終的にエラーなしで完了します。
コマンドラインからY.pyをコンパイルしようとすると、まったく異なることが起こります。 Y.pyのコンパイル中、コンパイラはY2を定義する前にimportステートメントをヒットします。次に、X.pyのコンパイルを開始します。すぐに、Y2を必要とするX.pyのimportステートメントにヒットします。ただし、Y2は未定義であるため、コンパイルは失敗します。
X.pyを変更してY1をインポートする場合、どのファイルをコンパイルしても、コンパイルは常に成功することに注意してください。ただし、ファイルY.pyを変更してシンボルX2をインポートすると、どちらのファイルもコンパイルされません。
モジュールX、またはXによってインポートされたモジュールが現在のモジュールをインポートする可能性がある場合は、以下を使用しないでください。
from X import Y
循環インポートがあると思われるときはいつでも、コンパイル時の他のモジュールの変数への参照も避けるべきです。無邪気な見た目のコードを考えてみましょう。
import X
z = X.Y
このモジュールがXをインポートする前にモジュールXがこのモジュールをインポートするとします。さらに、importステートメントの後にXでYが定義されているとします。このモジュールがインポートされると、Yは定義されず、コンパイルエラーが発生します。このモジュールが最初にYをインポートする場合、それを回避できます。しかし、同僚の1人が3番目のモジュールの定義の順序を無邪気に変更すると、コードが破損します。
場合によっては、importステートメントを他のモジュールで必要なシンボル定義の下に移動することにより、循環依存関係を解決できます。上記の例では、importステートメントの前の定義が失敗することはありません。 importステートメントの後の定義は、コンパイルの順序によっては失敗する場合があります。インポートされたシンボルがコンパイル時に必要ない限り、インポート文をファイルの最後に置くこともできます。
モジュール内でimportステートメントを下に移動すると、何をしているのかわかりにくくなることに注意してください。モジュールの上部に次のようなコメントを追加して、これを補います。
#import X (actual import moved down to avoid circular dependency)
一般的にこれは悪い習慣ですが、回避するのが難しい場合もあります。
私のように、Djangoからこの問題に出くわした方は、ドキュメントが解決策を提供していることを知っておく必要があります。 https://docs.djangoproject.com/en/1.10/ref/models/fields/ #foreignkey
「...別のアプリケーションで定義されたモデルを参照するには、完全なアプリケーションラベルでモデルを明示的に指定できます。たとえば、上記のメーカーモデルがproductionと呼ばれる別のアプリケーションで定義されている場合、以下を使用する必要があります:
class Car(models.Model):
manufacturer = models.ForeignKey(
'production.Manufacturer',
on_delete=models.CASCADE,
)
この種の参照は、2つのアプリケーション間の循環インポート依存関係を解決するときに役立ちます。... "
私は次を使用していました:
from module import Foo
foo_instance = Foo()
しかし、circular reference
を取り除くために、私は次のことをしましたが、うまくいきました:
import module.foo
foo_instance = foo.Foo()