파이썬 순환 가져 오기?


98

그래서이 오류가 발생합니다

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

동일한 import 문을 더 많이 사용하고 작동한다는 것을 알 수 있습니까? 순환 가져 오기에 대해 작성되지 않은 규칙이 있습니까? 동일한 클래스를 호출 스택 아래에서 어떻게 사용합니까?

답변:


161

나는 jpmc26의 대답이 결코 잘못된 것은 아니지만 순환 수입에 너무 많이 내려 간다고 생각합니다 . 올바르게 설정하면 제대로 작동 할 수 있습니다.

이를 수행하는 가장 쉬운 방법은 import my_module대신 구문 을 사용 하는 것 from my_module import some_object입니다. 전자는 my_module우리를 다시 수입 하더라도 거의 항상 작동 합니다. 후자 my_object는에 이미 정의 된 경우에만 작동 my_module하며 순환 가져 오기에서는 그렇지 않을 수 있습니다.

귀하의 사례에 대해 구체적으로 설명하십시오 . 직접적 으로가 아니라 entities/post.py할 일로 변경 import physics한 다음 참조하십시오 . 마찬가지로, 변경 해야 할 다음 사용 만이 아니라 .physics.PostBodyPostBodyphysics.pyimport entities.postentities.post.PostPost


5
이 답변은 상대적 수입품과 호환됩니까?
Joe

17
왜 이런 일이 발생합니까?
Juan Pablo Santos

4
from구문이 항상 작동 한다고 말하는 것은 잘못된 것입니다. class A(object): pass; class C(b.B): pass모듈 a와 class B(a.A): pass모듈 b 에 있으면 순환 가져 오기가 여전히 문제이며 작동하지 않습니다.
CrazyCasta

1
맞습니다. 모듈의 최상위 코드 (예 : 예제에서 클래스 선언의 기본 클래스)의 순환 종속성이 문제가 될 것입니다. 모듈 구성을 리팩토링해야한다는 jpmc의 답변이 아마도 100 % 정확할 수있는 상황입니다. 클래스 B를 module a로 이동하거나 클래스 C를 모듈로 이동 b하여주기를 중단 할 수 있습니다. 원의 한 방향에만 최상위 코드가 포함되어 있어도 (예 : 클래스 C가 존재하지 않는 경우 ) 다른 코드에서 먼저 가져온 모듈에 따라 오류 가 발생할 수 있습니다.
Blckknght 2015

2
@TylerCrompton : "모듈 가져 오기는 절대적이어야합니다"가 무슨 뜻인지 잘 모르겠습니다. 순환 상대 가져 오기는 내용이 아닌 모듈을 가져 오는 한 작동 할 수 있습니다 (예 : from . import sibling_modulenot from .sibling_module import SomeClass). 패키지의 __init__.py파일이 순환 가져 오기에 포함될 때 약간의 미묘함 이 있지만 문제는 드물고 import구현 상의 버그 일 수 있습니다. 패치를 제출 한 Python 버그 23447을 참조하십시오 (슬프게도 시들었습니다 ).
Blckknght

51

모듈 (또는 그 구성원)을 처음으로 가져올 때 모듈 내부의 코드는 다른 코드와 마찬가지로 순차적으로 실행됩니다. 예를 들어, 함수의 본문과 다르게 취급되지 않습니다. An import은 다른 명령 (할당, 함수 호출 def,, class) 과 같은 명령 입니다. 가져 오기가 스크립트 상단에서 발생한다고 가정하면 다음과 같은 상황이 발생합니다.

  • 당신이 가져올 때 World에서 worldworld스크립트가 실행됩니다.
  • world스크립트 수입 Field원인, entities.field스크립트가 실행하세요.
  • 이 프로세스는 entities.post가져 오기를 시도했기 때문에 스크립트에 도달 할 때까지 계속 됩니다.Post
  • entities.post스크립트 원인 physics은 수입하려고하기 때문에 모듈이 실행되는PostBody
  • 마지막으로, physics가져올 시도 Post에서entities.post
  • entities.post모듈이 아직 메모리에 있는지 확실 하지 않지만 실제로는 중요하지 않습니다. 모듈이 메모리에 없거나 정의 실행이 완료되지Post 않았기 때문에 모듈에 아직 멤버 가 없습니다.Post
  • 어느 쪽이든 Post가져올 것이 없기 때문에 오류가 발생합니다.

따라서 "콜 스택에서 더 많이 작동"하는 것이 아닙니다. 이것은 오류가 발생한 위치에 대한 스택 추적으로, Post해당 클래스에서 가져 오려는 중에 오류가 발생 했음을 의미합니다 . 순환 가져 오기를 사용하면 안됩니다. 기껏해야 미미한 이점 (일반적으로 이점 없음 )이 있으며 이와 같은 문제가 발생합니다. 그것은 그것을 유지하는 모든 개발자에게 부담을 주므로 깨지지 않도록 달걀 껍질 위를 걷도록 강요합니다. 모듈 조직을 리팩터링하십시오.


1
이어야 isinstance(userData, Post)합니다. 어쨌든 당신은 선택의 여지가 없습니다. 순환 가져 오기가 작동하지 않습니다. 순환 수입품이 있다는 사실은 나에게 코드 냄새입니다. 세 번째 모듈로 이동해야하는 일부 기능이 있음을 나타냅니다. 두 수업을 모두 보지 않고서는 무엇을 말할 수 없었습니다.
jpmc26

3
@CpILL 잠시 후 매우 험난한 옵션이 나에게 발생했습니다. 시간 제약이나 당신이 가지고있는 것 때문에 지금이 일을 할 없다면 , 그것을 사용하고있는 메서드 내에서 로컬로 가져올 있습니다. 함수 본문 def은 함수가 호출 될 때까지 실행되지 않으므로 실제로 함수를 호출 할 때까지 가져 오기가 발생하지 않습니다. 그때까지 import모듈 중 하나가 호출 전에 완전히 임포트되었으므로 s는 작동합니다. 그것은 절대적으로 역겨운 해킹이며 상당한 시간 동안 코드베이스에 남아 있어서는 안됩니다.
jpmc26

15
나는 당신의 대답이 순환 수입에 대해 너무 힘들다고 생각합니다. 당신은 그냥 할 경우 원형 수입은 보통 일 import foo이 아니라 from foo import Bar. 대부분의 모듈은 나중에 실행되는 항목 (예 : 함수 및 클래스)을 정의하기 때문입니다. 가져올 때 중요한 작업을 수행하는 모듈 (예 :로 보호되지 않는 스크립트 if __name__ == "__main__")은 여전히 ​​문제가 될 수 있지만 그렇게 일반적이지 않습니다.
Blckknght 2014 년

6
@Blckknght 나는 당신이 순환 수입을 사용하면 다른 사람들이 조사하고 혼란스러워해야 할 이상한 문제에 시간을 할애하고 있다고 생각합니다. 그들은 넘어지지 않도록주의하면서 시간을 보내도록 강요하며, 그 외에도 디자인에 리팩토링이 필요한 코드 냄새가 있습니다. 기술적으로 실현 가능한지에 대해서는 틀렸을 수도 있지만 조만간 문제를 일으킬 수있는 끔찍한 디자인 선택입니다. 명확성과 단순성은 프로그래밍에서 성배이며 순환 수입은 내 책에서 둘 다 위반합니다.
jpmc26

6
또는; 기능을 너무 많이 분할하여 순환 가져 오기의 원인입니다. 항상 서로 의존하는 두 가지가 있다면 ; 하나의 파일에 넣는 것이 가장 좋습니다. 파이썬은 자바가 아닙니다. 이상한 가져 오기 로직을 ​​방지하기 위해 기능 / 클래스를 단일 파일로 그룹화하지 않을 이유가 없습니다. :-)
Mark Ribau 2015 년

40

순환 종속성을 이해하려면 Python이 본질적으로 스크립팅 언어라는 점을 기억해야합니다. 메서드 외부의 명령문 실행은 컴파일 타임에 발생합니다. Import 문은 메서드 호출처럼 실행되며이를 이해하려면 메서드 호출처럼 생각해야합니다.

가져 오기를 수행 할 때 가져 오는 파일이 이미 모듈 테이블에 있는지 여부에 따라 발생하는 상황이 달라집니다. 그렇다면 파이썬은 현재 기호 테이블에있는 것을 사용합니다. 그렇지 않다면 파이썬은 모듈 파일을 읽기 시작하고 거기에서 찾은 모든 것을 컴파일 / 실행 / 가져옵니다. 컴파일 타임에 참조 된 심볼은 표시되었는지 여부에 따라 컴파일러에서 표시되는지 여부에 따라 달라집니다.

두 개의 소스 파일이 있다고 가정하십시오.

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의 import 문에 도달합니다. 이로 인해 컴파일러는 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는 정의되지 않았으므로 컴파일이 실패합니다.

Y1을 가져 오도록 X.py를 수정하면 어떤 파일을 컴파일하든 상관없이 컴파일이 항상 성공합니다. 그러나 파일 Y.py를 수정하여 기호 X2를 가져 오면 두 파일 모두 컴파일되지 않습니다.

모듈 X 또는 X에서 가져온 모듈이 현재 모듈을 가져올 수있는 경우 언제든지 다음을 사용하지 마십시오.

from X import Y

순환 가져 오기가 있다고 생각할 때마다 다른 모듈의 변수에 대한 컴파일 시간 참조도 피해야합니다. 무고 해 보이는 코드를 고려하십시오.

import X
z = X.Y

이 모듈이 X를 가져 오기 전에 모듈 X가이 모듈을 가져 온다고 가정합니다. 또한 Y가 import 문 뒤에 X에 정의되어 있다고 가정합니다. 그러면이 모듈을 가져올 때 Y가 정의되지 않고 컴파일 오류가 발생합니다. 이 모듈이 먼저 Y를 가져 오면 그만 둘 수 있습니다. 그러나 동료 중 한 명이 세 번째 모듈에서 정의 순서를 무고하게 변경하면 코드가 손상됩니다.

경우에 따라 import 문을 다른 모듈에 필요한 기호 정의 아래로 이동하여 순환 종속성을 해결할 수 있습니다. 위의 예에서 import 문 이전의 정의는 실패하지 않습니다. import 문 뒤의 정의는 컴파일 순서에 따라 때때로 실패합니다. 컴파일 타임에 가져온 심볼이 필요하지 않은 한 파일 끝에 import 문을 넣을 수도 있습니다.

모듈에서 import 문을 아래로 이동하면 수행중인 작업이 가려집니다. 모듈 맨 위에 다음과 같은 주석으로이를 보완하십시오.

#import X   (actual import moved down to avoid circular dependency)

일반적으로 이것은 나쁜 습관이지만 때로는 피하는 것이 어렵습니다.


2
나는 파이썬 컴파일러 나 컴파일 시간이 전혀 없다고 생각
pkqxdd

6
파이썬은 하지 컴파일러를 가지고 있고, 되어 컴파일이 단지 일반적으로 사용자로부터 멀리 숨겨진, @pkqxdd 컴파일. 이것은 약간 혼란 스러울 수 있지만, 저자가 파이썬의 다소 모호한 "컴파일 시간"에 대한 언급없이 무슨 일이 벌어지고 있는지에 대한 놀랍도록 분명한 설명을 제공하는 것은 어려울 것입니다.
Hank


나는 이것을 내 컴퓨터에서 시도해 보았고 다른 결과를 얻었습니다. X.py를 실행했지만 ''Y '에서'Y2 '이름을 가져올 수 없습니다'오류가 발생했습니다. 그래도 문제없이 Y.py를 실행했습니다. 저는 Python 3.7.5에 있습니다. 여기서 문제가 무엇인지 설명하는 데 도움이 될 수 있습니까?
xuefeng huang

18

저처럼 Django에서이 문제를 접한 분들은 문서가 해결책을 제공한다는 것을 알아야합니다 : https://docs.djangoproject.com/en/1.10/ref/models/fields/#foreignkey

"... 다른 애플리케이션에 정의 된 모델을 참조하려면 전체 애플리케이션 레이블이있는 모델을 명시 적으로 지정할 수 있습니다. 예를 들어 위의 제조업체 모델이 프로덕션이라는 다른 애플리케이션에 정의 된 경우 다음을 사용해야합니다.

class Car(models.Model):
    manufacturer = models.ForeignKey(
        'production.Manufacturer',
        on_delete=models.CASCADE,
)

이러한 종류의 참조는 두 응용 프로그램 간의 순환 가져 오기 종속성을 해결할 때 유용 할 수 있습니다. ... "


6
"감사합니다"라고 말하기 위해 댓글을 사용해서는 안된다는 것을 알고 있지만, 이것은 몇 시간 동안 저를 괴롭 혔습니다. 감사합니다, 감사합니다, 감사합니다 !!!
MikeyE

@MikeyE에 동의합니다. 나는 PonyORM으로 이것을 해결하기 위해 여러 블로그와 Stackoverflows를 읽었습니다. 다른 사람들이 그것이 나쁜 습관이라고 말하거나 왜 당신의 클래스를 순환하도록 코딩하겠습니까? ORM은 정확히 이것이 일어나는 곳입니다. 많은 예제가 모든 모델을 동일한 파일에 넣고 우리는 파일 당 모델을 사용하는 것을 제외하고는 이러한 예제를 따르기 때문에 Python이 컴파일에 실패 할 때 문제가 명확하지 않습니다. 하지만 답은 너무나 간단합니다. Mike가 지적했듯이 대단히 감사합니다.
trash80

4

이 모듈의 개체를 필요로하는 함수 내에서만 모듈을 가져올 수있었습니다.

def my_func():
    import Foo
    foo_instance = Foo()

파이썬의 우아함
Yaro

2

상당히 복잡한 앱에서이 문제가 발생하면 모든 가져 오기를 리팩터링하는 것이 번거로울 수 있습니다. PyCharm은 가져온 심볼의 모든 사용을 자동으로 변경하는 빠른 수정을 제공합니다.

여기에 이미지 설명 입력


0

나는 다음을 사용하고 있었다 :

from module import Foo

foo_instance = Foo()

그러나 제거하기 위해 circular reference다음을 수행했으며 작동했습니다.

import module.foo

foo_instance = foo.Foo()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.