순환 종속성을 이해하려면 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)
일반적으로 이것은 나쁜 습관이지만 때로는 피하는 것이 어렵습니다.