2つのモジュールが相互にインポートするとどうなりますか?
問題を一般化するために、Pythonの循環インポートについてはどうですか?
2つのモジュールが相互にインポートするとどうなりますか?
問題を一般化するために、Pythonの循環インポートについてはどうですか?
回答:
昨年comp.lang.pythonでこれについて本当に良い議論がありました。それはあなたの質問にかなり完全に答えます。
インポートは本当に簡単です。次のことを覚えておいてください。
'import'および 'from xxx import yyy'は実行可能なステートメントです。実行中のプログラムがその行に到達すると実行されます。
モジュールがsys.modulesにない場合、インポートによりsys.modulesに新しいモジュールエントリが作成され、モジュール内のコードが実行されます。実行が完了するまで、呼び出しモジュールに制御を返しません。
モジュールがsys.modulesに存在する場合、インポートは、実行が完了したかどうかに関係なく、単にそのモジュールを返します。これが、循環インポートが部分的に空のように見えるモジュールを返す可能性がある理由です。
最後に、実行中のスクリプトは__main__という名前のモジュールで実行され、独自の名前でスクリプトをインポートすると、__ main__とは無関係の新しいモジュールが作成されます。
この点をまとめると、モジュールをインポートするときに驚かないはずです。
import foo
内部bar
とimport bar
内部foo
で行う場合、それは正常に動作します。実際に何かが実行されるまでに、両方のモジュールが完全に読み込まれ、相互に参照されます。
代わりにあなたが行うときに問題があるfrom foo import abc
とfrom bar import xyz
。これは、各モジュールで、インポートする前に他のモジュールをインポートする必要があるためです(インポートする名前が存在するため)。
from foo import *
とfrom bar import *
も罰金を動作します。
from x import y
が、それでも循環インポートエラーが発生します
import
ステートメントの実行時に使用できる名前のみがインポートされます。したがって、エラーにはなりませんが、期待するすべての変数を取得できない場合があります。
from foo import *
とfrom bar import *
、して実行すべてがfoo
の初期化段階にあるbar
、との実際の機能をbar
まだ定義されていない...
循環インポートは終了しますが、モジュールの初期化中に循環インポートされたモジュールを使用しないように注意する必要があります。
次のファイルを検討してください。
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
b.pyの2番目のインポート(2番目のa in
)では、Pythonインタープリターはb
モジュールdictに既に存在するため、再度インポートされません。
あなたがアクセスしようとするb.x
からa
、モジュールの初期化中に、あなたが取得します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
です。これはの後でのみ発生します。
__name__
代わりに使用した場合、多くのメリットがあると思います'a'
。最初は、なぜファイルが2回実行されるのか完全に混乱していました。
他の回答がこのパターンを説明しているので、このパターンはPythonで受け入れられます:
def dostuff(self):
from foo import bar
...
これにより、ファイルが他のモジュールによってインポートされたときに、インポートステートメントの実行が回避されます。論理的な循環依存関係がある場合のみ、これは失敗します。
ほとんどの循環インポートは実際には論理的な循環インポートではなく、呼び出されたときにファイル全体の最上位のステートメントを評価ImportError
する方法が原因で、エラーを発生import()
させます。
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
はすでにインポートされているため、インポートキャッシュからプルします。
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
import bar
でfoo.py
終わりに
bar
とfoo
の両方を使用しなければならないgX
、「クリーンな」ソリューションを置くことであるgX
別のモジュールで両方持っているfoo
とbar
、インポートモジュールのことを。(意味的な依存関係が隠されていないという意味で最もクリーンです。)
bar
さえ見つけることができないからgX
です。循環インポート自体は問題gX
ありませんが、インポート時に定義されていないだけです。
モジュール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"
、プログラムが終了します。私はここで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")
繰り返しますが、これは恒久的な修正ではありませんが、コードをあまり変更せずにインポートエラーを修正したい場合に役立ちます。
乾杯!
ここにはたくさんの素晴らしい答えがあります。通常、問題に対する迅速な解決策がありますが、そのいくつかは他のものよりもPython的な感じがしますが、リファクタリングを実行する余裕がある場合は、別のアプローチは、コードの編成を分析し、循環依存関係を削除することです。たとえば、次のことがわかります。
ファイルa.py
from b import B
class A:
@staticmethod
def save_result(result):
print('save the result')
@staticmethod
def do_something_a_ish(param):
A.save_result(A.use_param_like_a_would(param))
@staticmethod
def do_something_related_to_b(param):
B.do_something_b_ish(param)
ファイルb.py
from a import A
class B:
@staticmethod
def do_something_b_ish(param):
A.save_result(B.use_param_like_b_would(param))
この場合、1つの静的メソッドを別のファイルに移動するだけですc.py
。
ファイルc.py
def save_result(result):
print('save the result')
除去可能になるsave_result
Aからの方法を、したがってB内からAのインポートを除去可能。
リファクタリングされたファイルa.py
from b import B
from c import save_result
class A:
@staticmethod
def do_something_a_ish(param):
A.save_result(A.use_param_like_a_would(param))
@staticmethod
def do_something_related_to_b(param):
B.do_something_b_ish(param)
リファクタリングされたファイルb.py
from c import save_result
class B:
@staticmethod
def do_something_b_ish(param):
save_result(B.use_param_like_b_would(param))
要約すると、静的である可能性のあるメソッドについてレポートするツール(たとえば、pylintまたはPyCharm)がある場合、staticmethod
それらにデコレータをスローするだけでは警告を止める最善の方法ではない可能性があります。メソッドはクラスに関連しているように見えますが、特に同じ機能を必要とする可能性がある密接に関連するモジュールがいくつかあり、DRYの原則を実践するつもりである場合は、メソッドを分離する方が良い場合があります。
インポートは2つのことを行うため、循環インポートは混乱を招く可能性があります。
前者は一度だけ実行され、後者は各インポート文で実行されます。循環インポートは、インポートモジュールが部分的に実行されたコードでインポートされたものを使用する場合に状況を作成します。その結果、インポート文の後に作成されたオブジェクトは表示されません。以下のコードサンプルはそれを示しています。
循環輸入は、いかなる犠牲を払っても避けられる究極の悪ではありません。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 __name__ == '__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, __name__ = {}".format(__name__)
x = 3
print 'b imports a'
import a
y = 5
print "b out"
a.py
print 'a in, __name__ = {}'.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, __name__ = b # b code execution started
b imports a
a in, __name__ = 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
わかりました、私にはかなりクールなソリューションがあると思います。file a
とfile があるとしましょうb
。あなたは持っているdef
か、class
ファイルにb
は、モジュールで使用することa
ができますが、何か他のものを持っている、のいずれかdef
、class
ファイルからまたは変数a
ファイルでの定義やクラスに必要であることb
。あなたはどうすることができ、ファイルの一番下に、あるa
ファイル内の関数やクラス呼び出した後、a
ファイルに必要とされb
ますが、ファイルから関数やクラスを呼び出す前にb
、ファイルのために必要なことをa
、言っimport b
次に、ここで重要な部分、ファイルからまたはファイルb
を必要とするファイル内のすべての定義またはクラスdef
class
a
(それを呼び出しましょうCLASS
)、あなたは言うfrom a import CLASS
これb
は、Pythonがfile内のインポートステートメントを実行せずにファイルをインポートできるためb
、循環インポートを回避できるため機能します。
例えば:
class A(object):
def __init__(self, name):
self.name = name
CLASS = A("me")
import b
go = B(6)
go.dostuff
class B(object):
def __init__(self, number):
self.number = number
def dostuff(self):
from a import CLASS
print "Hello " + CLASS.name + ", " + str(number) + " is an interesting number."
出来上がり。
from a import CLASS
a.pyのすべてのコードの実行は実際にはスキップされません。これが実際に起こることです:(1)a.pyのすべてのコードは特別なモジュール「__main__」として実行されます。(2)ではimport b
、b.pyの最上位コードが実行され(クラスBを定義)、制御が「__main__」に戻ります。(3)「__main__」は最終的に制御をに渡しgo.dostuff()
ます。(4)dostuff()がになるとimport a
、すべてのコードがa.pyで再び実行されます。今回はモジュール "a"として実行されます。次に、新しいモジュール「a」からCLASSオブジェクトをインポートします。したがって、実際には、これはimport a
b.pyのどこかで使用した場合と同じように機能します。