파이썬에서 순환 (또는 순환) 가져 오기


351

두 모듈이 서로 가져 오면 어떻게됩니까?

문제를 일반화하기 위해 파이썬에서 주기적 가져 오기는 어떻습니까?



1
또한 참고로 python 3.5 (및 아마도 그 이상)에서는 순환 가져 오기가 허용되지만 3.4 (및 아마도 아래)에서는 허용되지 않습니다.
Charlie Parker

4
파이썬 3.7.2를 사용하고 있으며 순환 종속성으로 인해 런타임 오류가 계속 발생합니다.
Richard Whitehead

답변:


280

작년 에 comp.lang.python 에서 이것에 대해 정말 좋은 토론이 있었습니다. 그것은 당신의 질문에 아주 철저하게 대답합니다.

수입품은 정말 간단합니다. 다음을 기억하십시오.

'import'및 'from xxx import yyy'는 실행 문입니다. 실행중인 프로그램이 해당 라인에 도달하면 실행됩니다.

모듈이 sys.modules에 없으면 가져 오기는 sys.modules에 새 모듈 항목을 작성한 다음 모듈에서 코드를 실행합니다. 실행이 완료 될 때까지 호출 모듈로 제어를 되 돌리지 않습니다.

sys.modules에 모듈이 있으면 가져 오기는 실행이 완료되었는지 여부에 관계없이 해당 모듈을 반환합니다. 순환 가져 오기가 부분적으로 비어있는 모듈을 리턴 할 수있는 이유입니다.

마지막으로, 실행 스크립트는 __main__이라는 모듈에서 실행되며 자체 이름으로 스크립트를 가져 오면 __main__과 관련이없는 새 모듈이 생성됩니다.

그 많은 것을 함께 가져 가면 모듈을 가져올 때 놀랄 일이 없습니다.


13
@meawoppl이 코멘트를 확장 해 주시겠습니까? 그들이 어떻게 구체적으로 바뀌 었습니까?
Dan Schien

3
현재 python3의 순환 가져 오기에 대한 유일한 참조 "새로운 기능" 페이지는 3.5 페이지에 있습니다. "상대 수입과 관련된 순환 수입이 지원됩니다"라고 표시됩니다. @meawoppl이 페이지에 나열되지 않은 내용이 있습니까?
zezollo

4
그들은 방어입니다. 3.0-3.4에서 지원되지 않습니다. 또는 적어도 성공의 의미가 다릅니다. 다음은 3.5 변경 사항을 언급하지 않은 시놉시스입니다. gist.github.com/datagrok/40bf84d5870c41a77dc6
meawoppl

"마지막으로, 실행 스크립트는 main 이라는 모듈에서 실행되며, 고유 한 이름으로 스크립트를 가져 오면 main 과 관련이없는 새 모듈이 생성됩니다 ." 따라서 파일이 a.py라고 말하고 기본 진입 점으로 실행될 때 가져 오기 변수와 같은 코드가 있으면 기본 파일입니다 . 그런 다음 동일한 파일 'a.py'가 sys 모듈 테이블에로드됩니까? 인쇄 문이라고 말하면 두 번 실행된다는 의미입니까? 메인 파일에 대해 한 번 그리고 가져 오기가 발생했을 때 다시?
변수

이 답변은 10 세이며, 내가 그것을 파이썬 2.x 또는 3.x를 다양한 버전의 올바른 유지하기 위해 현대화 업데이 트를하고 싶습니다
Fallenreaper

296

import foo내부 barimport bar내부 를 수행 foo하면 제대로 작동합니다. 실제로 모든 것이 실행될 때까지 두 모듈이 완전히로드되고 서로 참조됩니다.

대신 당신이 할 때 문제는 from foo import abcfrom bar import xyz. 이제 각 모듈에서 다른 모듈을 가져와야합니다 (가져 오기 이름이 존재하도록).


27
그것은 보인다 from foo import *그리고 from bar import *잘 작동합니다.
Akavall

1
a.py/b.py를 사용하여 위 게시물의 수정 사항을 확인하십시오. 그는 사용하지 않는 from x import y, 그럼에도 불구하고 여전히 원형 가져 오기 오류를 얻을 수
그렉 에니스

2
이것은 전적으로 사실이 아닙니다. import * from과 마찬가지로 순환 가져 오기에서 요소에 액세스하려고하면 최상위 레벨에서 스크립트 실행이 완료되기 전에 동일한 문제가 발생합니다. 예를 들어 한 패키지에서 다른 패키지로 패키지를 전역으로 설정하고 둘 다 서로를 포함하는 경우. 기본 클래스에서 객체가 여러 하위 클래스 중 하나 일 수 있고 사용 코드가 실제로 생성 된 것을 알 필요가없는 기본 클래스의 객체에 대한 느슨한 팩토리를 만들기 위해이 작업을 수행했습니다.
AaronM

3
@Akavall 실제로는 아닙니다. import명령문이 실행될 때 사용 가능한 이름 만 가져옵니다 . 따라서 오류가 발생하지 않지만 예상 한 모든 변수를 얻지 못할 수 있습니다.
augurar

3
만약 당신이 from foo import *하고 있다면 from bar import *,에서 실행되는 모든 것은 foo의 초기 단계에 bar있으며, 실제 기능 bar은 아직 정의되지 않았다 ...
Martian2049

100

주기적 가져 오기는 종료되지만 모듈 초기화 중에 주기적으로 가져온 모듈을 사용하지 않도록주의해야합니다.

다음 파일을 고려하십시오.

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합니다.


14
이것은 문제를 크게 설명하지만 해결책은 어떻습니까? 어떻게 x를 올바르게 가져오고 인쇄 할 수 있습니까? 위의 다른 해결책은 나를 위해 작동하지 않았다
mehmet

나는 당신이 __name__대신 사용한다면이 대답이 많은 도움이 될 것이라고 생각합니다 'a'. 처음에는 파일이 두 번 실행되는 이유가 완전히 혼란 스러웠습니다.
베르 기

30

다른 답변에서 설명 하듯 이이 패턴은 파이썬에서 허용됩니다.

def dostuff(self):
     from foo import bar
     ...

다른 모듈에서 파일을 가져올 때 import 문이 실행되지 않습니다. 논리적 순환 종속성이있는 경우에만 실패합니다.

대부분의 순환 가져 오기는 실제로 논리적 순환 가져 오기가 아니라 오히려 ImportError오류를 발생시킵니다.import() 호출 될 때 전체 파일의 최상위 문을 평가 .

이들은 ImportErrors당신이 적극적으로 정상에 당신의 수입을 원하는 경우 거의 항상 피할 수 있습니다 :

이 순환 수입을 고려하십시오.

앱 A

# profiles/serializers.py

from images.serializers import SimplifiedImageSerializer

class SimplifiedProfileSerializer(serializers.Serializer):
    name = serializers.CharField()

class ProfileSerializer(SimplifiedProfileSerializer):
    recent_images = SimplifiedImageSerializer(many=True)

앱 B

# 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:00python에서 순환 수입을 처리하는 방법은 다음과 같습니다.

try:
    from images.serializers import SimplifiedImageSerializer
except ImportError:
    import sys
    SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']

가져 오려고 SimplifiedImageSerializer하면ImportError 발생합니다 이미 수입되기 때문에, 그것은 importcache에서 그것을 가져옵니다.

추신 : David Beazley의 목소리로이 글 전체를 읽어야합니다.


9
모듈을 이미 가져온 경우 ImportError가 발생하지 않습니다. "import a; import a;"와 같이 원하는만큼 모듈을 가져올 수 있습니다. 괜찮습니다.
Yuras

9

나는 여기를 쳤다는 예를 얻었다!

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

2
이 문제를 어떻게 해결 했습니까? 나는 당신이하고있는 것과 매우 유사한 내 자신의 문제를 해결하기 위해 순환 가져 오기를 이해하려고 노력 하고 있습니다.
c089

12
얼름 ..이 엄청나게 못생긴 해킹으로 내 문제를 고쳤다 고 생각해 {{{sys.modules에서 'foo.bar'가 아닌 경우 : foo import bar에서 다른 경우 : bar = sys.modules [ 'foo.bar']}}} 개인적으로 순환 가져 오기는 잘못된 코드에 대한 거대한 경고 신호라고 생각합니다 디자인 ...
c089

5
@ c089, 아니면 그냥 움직일 수 import barfoo.py
warvariuc

5
경우 barfoo모두 사용해야 gX의 '깨끗한'솔루션을 배치하는 것입니다 gX다른 모듈에 모두가 foobar수입 모듈이 있습니다. (숨겨진 의미 론적 의존성이 없다는 점에서 가장 깨끗합니다.)
Tim Wilder

2
팀은 좋은 지적이 있습니다. 기본적으로 foo에서 bar찾을 수 없기 때문 gX입니다. 원형 가져 오기 자체는 괜찮지 만 가져 오기 gX할 때 정의되지 않았습니다.
Martian2049

9

모듈 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"를 실행하는 동안 한 줄씩 수행되는 작업은 다음과 같습니다.

  1. 첫 번째 줄은 import b입니다. 모듈 b를 방문합니다
  2. 모듈 b의 첫 번째 줄은 import a입니다. 모듈 A를 방문합니다
  3. 모듈 a의 첫 번째 줄은 import b그러나이 줄은 더 이상 다시 실행되지 않습니다 . 파이썬의 모든 파일은 가져 오기 줄을 한 번만 실행하기 때문에 언제 어디서 실행되는지는 중요하지 않습니다. 다음 행으로 넘어 가서 인쇄 "This is from module a"합니다.
  4. 모듈 b에서 전체 모듈 a를 방문한 후에도 여전히 모듈 b에 있습니다. 다음 줄이 인쇄됩니다"This is from module b"
  5. 모듈 b 라인이 완전히 실행됩니다. 모듈 b를 시작한 모듈 a로 돌아갑니다.
  6. 가져 오기 b 행이 이미 실행되었으며 다시 실행되지 않습니다. 다음 줄이 인쇄 "This is from module a"되고 프로그램이 완료됩니다.

4

나는 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")

다시 말하지만 이것은 영구적 수정은 아니지만 너무 많은 코드를 변경하지 않고 가져 오기 오류를 수정하려는 사람에게 도움이 될 수 있습니다.

건배!


3

여기에 많은 훌륭한 답변이 있습니다. 일반적으로 문제에 대한 빠른 해결책이 있지만 그중 일부는 다른 것보다 더 비판적이라고 생각하지만 리팩토링을하는 데 사치가 있다면 다른 방법은 코드 구성을 분석하고 순환 종속성을 제거하는 것입니다. 예를 들어 다음과 같은 것을 발견 할 수 있습니다.

파일 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_resultA 메소드를 있으므로 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 원칙을 실행하려는 경우 메소드를 분리하는 것이 좋습니다.


2

가져 오기가 두 가지 작업을 수행하므로 순환 가져 오기가 혼동 될 수 있습니다.

  1. 가져온 모듈 코드를 실행합니다.
  2. 가져 오기 모듈을 가져 오기 모듈 전역 기호 테이블에 추가

전자는 한 번만 수행되고 후자는 각 가져 오기 문에서 수행됩니다. 원형 가져 오기는 가져 오기 모듈이 가져온 코드 중 일부를 가져온 모듈을 사용할 때 상황을 만듭니다. 결과적으로 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

1

다음과 같은 방법으로 문제를 해결했으며 오류없이 잘 작동합니다. 두 개의 파일을 고려 a.py하고b.py .

나는 이것을 추가 a.py했고 효과가 있었다.

if __name__ == "__main__":
        main ()

a.py :

import b
y = 2
def main():
    print ("a out")
    print (b.x)

if __name__ == "__main__":
    main ()

b.py :

import a
print ("b out")
x = 3 + a.y

내가 얻는 결과는

>>> b out 
>>> a out 
>>> 5

0

좋아, 나는 꽤 멋진 해결책이 있다고 생각한다. file a과 file 이 있다고 가정 해 봅시다 b. 당신은이 def또는를 class파일에 b이 모듈에 사용하려는 a, 그러나 당신은 다른 뭔가를 가지고 중 하나 def, class파일이나 변수 a는 파일에 사용자 정의 또는 클래스에 필요 b. 당신이 할 수있는 것은 파일의 맨 아래에,이다 a, 파일에있는 함수 또는 클래스 호출 한 후 a파일에 필요 b하지만 파일에서 함수 나 클래스를 호출하기 전에 b이 파일을 필요로 aimport b 다음, 여기에 있습니다 중요한 부분 파일의 정의 나 모든 클래스에 b이 필요 def하거나class 파일을a(전화하자 CLASS)from a import CLASS

이것은 b파이썬이 file 에서 import 문을 실행하지 않고 파일 을 가져올 수 있기 때문에 b순환 가져 오기를 피할 수 있기 때문에 작동합니다.

예를 들면 다음과 같습니다.

파일 a :

class A(object):

     def __init__(self, name):

         self.name = name

CLASS = A("me")

import b

go = B(6)

go.dostuff

파일 b :

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 aa.py의 모든 코드를 다시 실행합니다 . 그런 다음 새 모듈 "a"에서 CLASS 객체를 가져옵니다. 따라서 실제로 import ab.py의 어느 곳에서나 사용하면 동등하게 작동합니다.
Matthias Fripp 2018 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.