Python 순환 가져오기
이 문서에서는 순환 가져오기가 일반적으로 발생하는 방식과 이를 수정하는 주요 방법을 알아봅니다. 또한 Python의 유형 힌트로 인해 발생하는 순환 가져오기 오류를 수정하는 방법도 살펴봅니다.
Python에서 순환 가져오기 오류 재현
모듈을 가져오려고 할 때 오류가 발생한 적이 있습니까? 이 경우 아래와 같은 오류가 발생합니다.
from My_Module_A import My_FUNC_A
def My_main_Func():
My_FUNC_A()
if __name__ == "main":
My_main_Func()
출력:
ImportError: cannot import name 'My_FUNC_A' from partially initialized module 'My_Module_A' (most likely due to a circular import)
가장 가능성이 높은 원인은 순환 수입임을 알려줍니다. 다음은 메인이 My_Module_A
에서 무언가를 가져오려고 시도할 때 이러한 종류의 가져오기 주기가 발생할 수 있는 가장 간단하고 가장 일반적인 방법입니다.
따라서 My_Module_A
초기화를 시작하고 My_Module_A
에서 코드를 실행하지만 My_Module_A
의 첫 번째 줄은 MY_Module_B
모듈에서 무언가를 가져오는 것입니다.
따라서 MY_Module_B
가 먼저 초기화되어야 하므로 My_Module_A
모듈 초기화를 중지합니다. 그런 다음 MY_Module_B
로 건너뛰고 대신 해당 코드를 실행하기 시작합니다.
그러나 MY_Module_B
모듈의 첫 번째 라인은 My_Module_A
의 무언가가 필요하므로 MY_Module_B
실행을 중지하고 My_Module_A
로 넘어갑니다.
그리고 My_Module_A
초기화를 시작하려고 하지만 My_Module_A
가 이미 초기화 프로세스에 있음을 알게 됩니다. 그래서 그것이 오류가 발생하는 곳입니다.
역추적에서 이 일련의 사건이 펼쳐지는 것을 볼 수 있습니다. 여기 main.py
에서 볼 수 있습니다. 그것은 MY_Module_B
모듈에서 MY_Func_B2
기능 가져오기를 트리거한 My_Module_A
모듈에서 My_FUNC_A
기능 가져오기를 시도합니다.
그리고 MY_Module_B.py
는 최종 오류가 발생한 My_Module_A
모듈에서 My_FUNC_A
함수 가져오기를 다시 트리거했습니다.
Traceback (most recent call last):
File "c:\Users\Dell\Desktop\demo\main.py", line 1, in <module>
from My_Module_A import My_FUNC_A
File "c:\Users\Dell\Desktop\demo\My_Module_A.py", line 1, in <module>
from MY_Module_B, import MY_Func_B2
File "c:\Users\Dell\Desktop\demo\MY_Module_B.py", line 1, in <module>
from My_Module_A import My_FUNC_A
ImportError: cannot import name 'My_FUNC_A' from partially initialized module 'My_Module_A' (most likely due to a circular import) (c:\Users\Dell\Desktop\demo\My_Module_A.py)
모듈 My_Module_A
와 MY_Module_B
사이에 가져오기 시간 주기가 있지만 가져온 이름을 보면 가져오기 시간에 사용하지 않습니다.
이 함수 My_FUNC_A()
또는 이 함수 MY_Func_B2()
가 호출될 때 사용합니다. 가져오기 시간에 이러한 함수를 호출하지 않기 때문에 진정한 순환 종속성이 없으므로 이 가져오기 주기를 제거할 수 있습니다.
Python에서 순환 가져오기 오류 해결
이 문제를 해결하는 세 가지 일반적인 방법이 있습니다. 첫 번째는 가져오기를 가져와서 함수 안에 넣는 것입니다.
Python에서는 함수가 호출될 때를 포함하여 언제든지 가져오기가 발생할 수 있습니다. 이 함수 MY_Func_B1()
가 필요한 경우 My_FUNC_A()
함수 내부의 가져오기일 수 있습니다.
def My_FUNC_A():
from MY_Module_B import MY_Func_B1
MY_Func_B1()
My_FUNC_A()
함수를 호출하지 않으면 MY_Func_B1
을 전혀 가져올 필요가 없기 때문에 훨씬 더 효율적일 수 있습니다. 예를 들어 My_Module_A
에 다양한 함수가 있고 많은 다른 함수가 MY_Func_B1()
함수를 사용한다고 가정해 보겠습니다.
이 경우 더 나은 솔루션은 MY_Module_B
모듈을 직접 가져오고 이를 사용하는 함수에서 MY_Module_B.MY_Func_B1()
과 같은 더 긴 이름을 사용하는 것입니다. 따라서 사이클에 관련된 모든 모듈에 대해 이 변환을 수행하십시오.
import MY_Module_B
def My_FUNC_A():
MY_Module_B.MY_Func_B1()
런타임 시 가져오기 주기 오류가 어떻게 해결되는지 살펴보겠습니다. 먼저 My_main_Func()
함수는 My_Module_A
에서 무언가를 가져오려고 시도하여 My_Module_A
실행을 시작합니다. 그런 다음 My_Module_A.py
에서 MY_Module_B
모듈을 가져오면 MY_Module_B
가 실행을 시작합니다.
MY_Module_B.py
에서 가져오기 모듈 My_Module_A
에 도달하면 모듈이 이미 초기화를 시작했습니다. 이 모듈 개체는 기술적으로 존재합니다. 존재하므로 재실행을 시작하지 않습니다.
그것이 존재하므로 MY_Module_B
모듈이 계속해서 가져오기를 완료한 다음 가져오기가 완료되면 My_Module_A
모듈 가져오기가 완료됩니다.
출력:
15
이 가져오기 주기를 제거하는 세 번째이자 마지막 일반적인 방법은 두 모듈을 병합하는 것입니다. 다시 말하지만, 가져오기도 없고 가져오기 주기도 없습니다. 그러나 처음에 두 개 또는 그 이상의 모듈이 있었다면 그럴만한 이유가 있었을 것입니다.
우리는 모든 모듈을 가져와서 하나로 병합할 것을 권장하지 않습니다. 그것은 어리석은 일입니다. 따라서 처음 두 가지 방법 중 하나를 사용하는 것이 좋습니다.
My_Module_A.py
파일의 소스 코드.
import MY_Module_B
def My_FUNC_A():
print(MY_Module_B.MY_Func_B1())
MY_Module_B.py
파일의 소스 코드.
import My_Module_A
def MY_Func_B1():
return 5 + 10
def MY_Func_B2():
My_Module_A.My_FUNC_A()
main.py
파일의 소스 코드.
from My_Module_A import My_FUNC_A
def My_main_Func():
My_FUNC_A()
if __name__ == "main":
My_main_Func()
Python에서 유형 힌트로 인한 순환 가져오기 오류 해결
유형 힌트를 사용하여 실행하게 될 다음과 같은 가장 일반적인 유형의 가져오기 주기인 다른 예를 살펴보겠습니다. 이 예제는 유형 주석이 함수 또는 클래스 정의에서 정의되기 때문에 이전 예제와 다릅니다.
함수가 정의되거나 클래스가 정의되었지만 실행할 때 정의되지 않은 경우 유형 힌트의 가장 일반적인 사용 사례는 런타임에도 없습니다. 우리가 관심 있는 모든 것이 정적 분석이라면 런타임에 가져오기를 수행할 필요조차 없습니다.
typing
모듈은 이 변수를 제공합니다. TYPE_CHECKING
은 런타임 시 거짓 상수입니다. 따라서 유형 검사에 필요한 모든 가져오기를 이들 중 하나에 넣으면 런타임에 아무 것도 발생하지 않습니다.
가져오기 루프를 피하면서 이 클래스 Mod_A_class
를 가져오면 이 이름 Mod_A_class
를 가져오지 않았기 때문에 이름 오류가 발생합니다.
출력:
Traceback (most recent call last):
File "c:\Users\Dell\Desktop\demo\main.py", line 1, in <module>
from My_Module_A import My_FUNC_A
File "c:\Users\Dell\Desktop\demo\My_Module_A.py", line 9, in <module>
from MY_Module_B, import MY_Func_B1
File "c:\Users\Dell\Desktop\demo\MY_Module_B.py", line 22, in <module>
class Mod_b_class:
File "c:\Users\Dell\Desktop\demo\MY_Module_B.py", line 23, in Mod_b_class
def __init__(self,a : Mod_A_class):
NameError: name 'Mod_A_class' is not defined
이 문제를 해결하려면 __future__
import annotations
에서 이것을 추가하십시오. 이게 뭐야?
주석이 작동하는 방식을 변경합니다. Mod_b_class
를 이름으로 평가하는 대신 모든 주석이 문자열로 변환됩니다.
이제 Mod_b_class
라는 이름을 가져오지 않았더라도 Mod_b_class
문자열이 확실히 존재하기 때문에 런타임 시 오류가 발생하지 않습니다. 그러나 __future__
가져오기에서 이러한 작업 중 하나를 수행할 때마다 항상 동작이 보장되는 것은 아닙니다.
이제 이렇게 하면 주석이 문자열로 처리되지만 이를 느리게 평가하도록 하는 경쟁 제안이 있습니다. 따라서 런타임에 유형 힌트를 보고 검사하지 않는 한 평가되지 않습니다.
이는 코드 기반에 아무런 차이가 없을 수 있지만 여전히 알고 있어야 합니다. 대체 솔루션은 모듈 가져오기에서
대신 모듈 가져오기
를 다시 사용하는 것이며 여전히 __future__
에서 주석
이 필요합니다.
My_Module_A.py
모듈의 완전한 소스 코드.
# from __future__ import annotations
# from typing import TYPE_CHECKING
# from MY_Module_B import MY_Func_B1
# if TYPE_CHECKING:
# from MY_Module_B import Mod_b_class
# def My_FUNC_A():
# b : Mod_b_class = MY_Func_B1()
# class Mod_A_class:
# def __init__(self,b : Mod_b_class):
# self.b=b
from __future__ import annotations
import MY_Module_B
def My_FUNC_A():
b: MY_Module_B.Mod_b_class = MY_Module_B.MY_Func_B1()
class Mod_A_class:
def __init__(self, b: MY_Module_B.Mod_b_class):
self.b = b
MY_Module_B.py
모듈의 완전한 소스 코드.
from __future__ import annotations
import My_Module_A
def MY_Func_B1():
...
class Mod_b_class:
def __init__(self, a: My_Module_A.Mod_A_class):
self.a = a
main.py
파일의 완전한 소스 코드.
from My_Module_A import My_FUNC_A
def My_main_Func():
My_FUNC_A()
if __name__ == "main":
My_main_Func()
Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.
LinkedIn