두 모듈이 서로 가져 오면 어떻게됩니까?
문제를 일반화하기 위해 파이썬에서 주기적 가져 오기는 어떻습니까?
두 모듈이 서로 가져 오면 어떻게됩니까?
문제를 일반화하기 위해 파이썬에서 주기적 가져 오기는 어떻습니까?
답변:
작년 에 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의 두 번째 가져 오기 (두 번째 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'
. 처음에는 파일이 두 번 실행되는 이유가 완전히 혼란 스러웠습니다.
다른 답변에서 설명 하듯 이이 패턴은 파이썬에서 허용됩니다.
def dostuff(self):
from foo import bar
...
다른 모듈에서 파일을 가져올 때 import 문이 실행되지 않습니다. 논리적 순환 종속성이있는 경우에만 실패합니다.
대부분의 순환 가져 오기는 실제로 논리적 순환 가져 오기가 아니라 오히려 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
발생합니다 이미 수입되기 때문에, 그것은 importcache에서 그것을 가져옵니다.
추신 : 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
그러나이 줄은 더 이상 다시 실행되지 않습니다 . 파이썬의 모든 파일은 가져 오기 줄을 한 번만 실행하기 때문에 언제 어디서 실행되는지는 중요하지 않습니다. 다음 행으로 넘어 가서 인쇄 "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")
다시 말하지만 이것은 영구적 수정은 아니지만 너무 많은 코드를 변경하지 않고 가져 오기 오류를 수정하려는 사람에게 도움이 될 수 있습니다.
건배!
여기에 많은 훌륭한 답변이 있습니다. 일반적으로 문제에 대한 빠른 해결책이 있지만 그중 일부는 다른 것보다 더 비판적이라고 생각하지만 리팩토링을하는 데 사치가 있다면 다른 방법은 코드 구성을 분석하고 순환 종속성을 제거하는 것입니다. 예를 들어 다음과 같은 것을 발견 할 수 있습니다.
파일 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))
이 경우 하나의 정적 메소드를 별도의 파일로 이동하면됩니다 c.py
.
파일 c.py
def save_result(result):
print('save the result')
제거를 허용합니다 save_result
A 메소드를 있으므로 b에서 a에서 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 원칙을 실행하려는 경우 메소드를 분리하는 것이 좋습니다.
가져 오기가 두 가지 작업을 수행하므로 순환 가져 오기가 혼동 될 수 있습니다.
전자는 한 번만 수행되고 후자는 각 가져 오기 문에서 수행됩니다. 원형 가져 오기는 가져 오기 모듈이 가져온 코드 중 일부를 가져온 모듈을 사용할 때 상황을 만듭니다. 결과적으로 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 __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
다음과 같은 방법으로 문제를 해결했으며 오류없이 잘 작동합니다. 두 개의 파일을 고려 a.py
하고b.py
.
나는 이것을 추가 a.py
했고 효과가 있었다.
if __name__ == "__main__":
main ()
import b
y = 2
def main():
print ("a out")
print (b.x)
if __name__ == "__main__":
main ()
import a
print ("b out")
x = 3 + a.y
내가 얻는 결과는
>>> b out
>>> a out
>>> 5
좋아, 나는 꽤 멋진 해결책이 있다고 생각한다. 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
파이썬이 file 에서 import 문을 실행하지 않고 파일 을 가져올 수 있기 때문에 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 ()가 오면 이번에는 모듈 "a"와 같이 import a
a.py의 모든 코드를 다시 실행합니다 . 그런 다음 새 모듈 "a"에서 CLASS 객체를 가져옵니다. 따라서 실제로 import a
b.py의 어느 곳에서나 사용하면 동등하게 작동합니다.