[python] 파이썬 순환 가져 오기?

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

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



답변

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


답변

모듈 (또는 그 구성원)을 처음으로 가져올 때 모듈 내부의 코드는 다른 코드와 마찬가지로 순차적으로 실행됩니다. 예를 들어, 함수의 본문과 다르게 취급되지 않습니다. 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해당 클래스에서 가져 오려는 중에 오류가 발생 했음을 의미합니다 . 순환 가져 오기를 사용하면 안됩니다. 기껏해야 미미한 이점 (일반적으로 이점 없음 )이 있으며 이와 같은 문제가 발생합니다. 그것은 그것을 유지하는 모든 개발자에게 부담을 주므로 깨지지 않도록 달걀 껍질 위를 걷도록 강요합니다. 모듈 조직을 리팩터링하십시오.


답변

순환 종속성을 이해하려면 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)

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


답변

저처럼 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,
)

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


답변

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

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


답변

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

여기에 이미지 설명 입력


답변

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

from module import Foo

foo_instance = Foo()

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

import module.foo

foo_instance = foo.Foo()