2つのモジュールが相互にインポートするとどうなりますか?
問題を一般化するために、Pythonの循環インポートについてはどうですか?
これについては昨年 comp.lang.python で非常に良い議論がありました。それはあなたの質問にかなり徹底的に答えます。
インポートは本当に簡単です。次のことを覚えておいてください。
「import」および「from xxx import yyy」は実行可能ステートメントです。実行中のプログラムがその行に到達すると実行されます。
モジュールがsys.modulesにない場合、インポートはsys.modulesに新しいモジュールエントリを作成し、モジュール内のコードを実行します。実行が完了するまで、呼び出しモジュールに制御を返しません。
モジュールがsys.modulesに存在する場合、インポートは実行が完了したかどうかに関係なくそのモジュールを返します。これが、循環インポートが部分的に空に見えるモジュールを返す理由です。
最後に、実行中のスクリプトは__main__というモジュールで実行され、独自の名前でスクリプトをインポートすると、__ main__とは無関係の新しいモジュールが作成されます。
その多くを一緒に取り込んで、モジュールをインポートするときに驚くことはないはずです。
bar
内でimport foo
を実行し、foo
内でimport bar
を実行すると、正常に動作します。実際に何かが実行されるまでに、両方のモジュールは完全にロードされ、相互に参照します。
問題は、代わりにfrom foo import abc
とfrom bar import xyz
を行うときです。なぜなら、各モジュールは、インポートする前に(インポートする名前が存在するように)既にインポートされている他のモジュールを必要とするからです。
循環インポートは終了しますが、モジュールの初期化中に循環インポートされたモジュールを使用しないように注意する必要があります。
以下のファイルを考慮してください。
a.py:
print "a in"
import sys
print "b imported: %s" % ("b" in sys.modules, )
import b
print "a out"
b.py:
print "b in"
import a
print "b out"
x = 3
A.pyを実行すると、次のものが得られます。
$ python a.py
a in
b imported: False
b in
a in
b imported: True
a out
b out
a out
2番目のb.pyのインポート(2番目のa in
)では、Pythonインタープリターはb
を再びインポートしません。モジュールdictに既に存在するためです。
モジュールの初期化中にa
からb.x
にアクセスしようとすると、AttributeError
が取得されます。
次の行をa.py
に追加します。
print b.x
次に、出力は次のとおりです。
$ python a.py
a in
b imported: False
b in
a in
b imported: True
a out
Traceback (most recent call last):
File "a.py", line 4, in <module>
import b
File "/home/shlomme/tmp/x/b.py", line 2, in <module>
import a
File "/home/shlomme/tmp/x/a.py", line 7, in <module>
print b.x
AttributeError: 'module' object has no attribute 'x'
これは、モジュールがインポート時に実行され、b.x
にアクセスした時点で、x = 3
行がまだ実行されていないためです。これはb out
の後にのみ発生します。
他の答えが説明しているように、このパターンはPythonで受け入れられます:
def dostuff(self):
from foo import bar
...
これにより、ファイルが他のモジュールによってインポートされるときにimportステートメントの実行が回避されます。論理的な循環依存関係がある場合のみ、これは失敗します。
ほとんどの循環インポートは、実際には論理循環インポートではなく、import()
が呼び出されたときにファイル全体の最上位ステートメントを評価する方法のために、ImportError
エラーを発生させます。
これらのImportErrors
は、インポートを確実に上にしたい場合はほとんど常に回避できます:
この循環インポートを検討してください。
# profiles/serializers.py
from images.serializers import SimplifiedImageSerializer
class SimplifiedProfileSerializer(serializers.Serializer):
name = serializers.CharField()
class ProfileSerializer(SimplifiedProfileSerializer):
recent_images = SimplifiedImageSerializer(many=True)
# images/serializers.py
from profiles.serializers import SimplifiedProfileSerializer
class SimplifiedImageSerializer(serializers.Serializer):
title = serializers.CharField()
class ImageSerializer(SimplifiedImageSerializer):
profile = SimplifiedProfileSerializer()
David Beazleysの優れた講演から モジュールとパッケージ:Live and Let Die!-PyCon 2015 、1:54:00
、ここにPythonの循環インポートを処理する方法があります:
try:
from images.serializers import SimplifiedImageSerializer
except ImportError:
import sys
SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']
これはSimplifiedImageSerializer
をインポートしようとします。ImportError
が既にインポートされているために発生した場合、それはimportcacheからプルされます。
PS:この投稿全体をDavid Beazleyの声で読む必要があります。
ここで私に衝撃を与えた例がありました!
foo.py
import bar
class gX(object):
g = 10
bar.py
from foo import gX
o = gX()
main.py
import foo
import bar
print "all done"
コマンドラインで: $ python main.py
Traceback (most recent call last):
File "m.py", line 1, in <module>
import foo
File "/home/xolve/foo.py", line 1, in <module>
import bar
File "/home/xolve/bar.py", line 1, in <module>
from foo import gX
ImportError: cannot import name gX
私はここでのpythoneerの答えに完全に同意します。しかし、循環インポートに欠陥があり、ユニットテストを追加しようとすると問題が発生するコードを見つけました。したがって、すべてを変更せずにすばやくパッチを適用するには、動的インポートを実行して問題を解決できます。
# Hack to import something without circular import issue
def load_module(name):
"""Load module using imp.find_module"""
names = name.split(".")
path = None
for name in names:
f, path, info = imp.find_module(name, path)
path = [path]
return imp.load_module(name, f, path[0], info)
constants = load_module("app.constants")
繰り返しますが、これは永続的な修正ではありませんが、コードをあまり変更せずにインポートエラーを修正したい人を助けるかもしれません。
乾杯!
モジュールa.py:
import b
print("This is from module a")
モジュールb.py
import a
print("This is from module b")
「モジュールa」を実行すると、次が出力されます。
>>>
'This is from module a'
'This is from module b'
'This is from module a'
>>>
この3行を出力しましたが、循環インポートのために不定詞を出力するはずでした。 「モジュールa」の実行中に行ごとに何が起こるかを以下に示します。
import b
です。モジュールbにアクセスしますimport a
です。モジュールaにアクセスしますimport b
ですが、この行はもう実行されないことに注意してください pythonインポート行を一度だけ実行します。どこで、いつ実行されるかは関係ありません。次の行に渡され、"This is from module a"
が出力されます。"This is from module b"
を出力します"This is from module a"
を出力し、プログラムは終了します。次の方法で問題を解決しましたが、エラーなく正常に機能します。 2つのファイルa.py
とb.py
を考えてください。
これをa.py
に追加しましたが、うまくいきました。
if __== "__main__":
main ()
import b
y = 2
def main():
print ("a out")
print (b.x)
if __== "__main__":
main ()
import a
print ("b out")
x = 3 + a.y
私が得る出力は
>>> b out
>>> a out
>>> 5
インポートは2つのことを行うため、循環インポートは混乱を招く可能性があります。
前者は1回だけ実行され、後者は各importステートメントで実行されます。インポートモジュールが部分的に実行されたコードでインポートされたモジュールを使用する場合、循環インポートは状況を作成します。その結果、importステートメントの後に作成されたオブジェクトは表示されません。以下のコードサンプルはそれを示しています。
循環輸入は、いかなる犠牲を払っても回避される究極の悪ではありません。 Flaskのような一部のフレームワークでは非常に自然であり、コードを調整してそれらを削除してもコードは改善されません。
main.py
print 'import b'
import b
print 'a in globals() {}'.format('a' in globals())
print 'import a'
import a
print 'a in globals() {}'.format('a' in globals())
if __== '__main__':
print 'imports done'
print 'b has y {}, a is b.a {}'.format(hasattr(b, 'y'), a is b.a)
b.by
print "b in, __= {}".format(__name__)
x = 3
print 'b imports a'
import a
y = 5
print "b out"
a.py
print 'a in, __= {}'.format(__name__)
print 'a imports b'
import b
print 'b has x {}'.format(hasattr(b, 'x'))
print 'b has y {}'.format(hasattr(b, 'y'))
print "a out"
コメント付きのpython main.py出力
import b
b in, __= b # b code execution started
b imports a
a in, __= a # a code execution started
a imports b # b code execution is already in progress
b has x True
b has y False # b defines y after a import,
a out
b out
a in globals() False # import only adds a to main global symbol table
import a
a in globals() True
imports done
b has y True, a is b.a True # all b objects are available