Python 순환 가져오기

Salman Mehmood 2024년2월15일
  1. Python에서 순환 가져오기 오류 재현
  2. Python에서 순환 가져오기 오류 해결
  3. Python에서 유형 힌트로 인한 순환 가져오기 오류 해결
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에서 무언가를 가져오려고 시도할 때 이러한 종류의 가져오기 주기가 발생할 수 있는 가장 간단하고 가장 일반적인 방법입니다.

python 순환 가져오기 - 예제 1

따라서 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_AMY_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 모듈 가져오기가 완료됩니다.

python 순환 가져오기 - 예제 2

출력:

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를 가져오지 않았기 때문에 이름 오류가 발생합니다.

python 순환 가져오기 - 예제 3

출력:

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를 이름으로 평가하는 대신 모든 주석이 문자열로 변환됩니다.

python 순환 가져오기 - 예 4

이제 Mod_b_class라는 이름을 가져오지 않았더라도 Mod_b_class 문자열이 확실히 존재하기 때문에 런타임 시 오류가 발생하지 않습니다. 그러나 __future__ 가져오기에서 이러한 작업 중 하나를 수행할 때마다 항상 동작이 보장되는 것은 아닙니다.

이제 이렇게 하면 주석이 문자열로 처리되지만 이를 느리게 평가하도록 하는 경쟁 제안이 있습니다. 따라서 런타임에 유형 힌트를 보고 검사하지 않는 한 평가되지 않습니다.

이는 코드 기반에 아무런 차이가 없을 수 있지만 여전히 알고 있어야 합니다. 대체 솔루션은 모듈 가져오기에서 대신 모듈 가져오기를 다시 사용하는 것이며 여전히 __future__에서 주석이 필요합니다.

python 순환 가져오기 - 예 5

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()
Salman Mehmood avatar Salman Mehmood avatar

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

관련 문장 - Python Import