brunch

You can make anything
by writing

C.S.Lewis

by 매스프레소 Jan 08. 2020

Python Circular Imports

매스프레소에서는 대부분의 서버가 Python/Django 기반이기 때문에 Python Circular Imports가 발생하는 경우를 많이 볼 수 있습니다. 그래서 오늘은 Python Circular Imports가 무엇이며, 자주 일어나는 상황과 그에 따른 해결책을 얘기하려고 합니다.




Python Circular Imports?


Python Circular Imports은 한국말로는 순환 참조 정도로 번역이 됩니다. 두 개의 모듈이 서로를 참조하는 경우 발생합니다. 예를 들어 A 가 B를 참조하고, B가 A를 참조하는 경우를 말합니다. 코드로 예를 들어보겠습니다.



위의 a.py와 b.py를 각각 실행하면,


a.py는 순환 참조로 인한 에러가 발생하고, b.py의 경우 정상 작동합니다.

a.py에서 발생하는 이슈를 순환 참조로 인한 오류라고 합니다.




import time 과 runtime


a.py와 b.py 의 차이를 제대로 이해하려면 import time과 runtime을 알아보겠습니다.

Python은 프로그램 실행시 import time과 runtime이 구분되어 실행됩니다. rumtime의 경우 실제로 모든 코드가 실행되는 순간입니다. 그에 반해 import time에는 .py 모듈에 들어 있는 소스 코드를 위에서 부터 순서대로 한 번 파싱하고, 실행을 위한 바이트 코드를 생성합니다. import time의 결과물로 .pyc 파일이 생성되며, Syntax Error 도 코드를 파싱하는 과정에서 일어납니다. 또한 함수 본문과 클래스의 메소드 본문을 제외한 대부분의 코드를 import time에 실행합니다. (최신 .pyc 파일이 존재할 경우 이 과정이 생략됩니다)

즉, import time에는 함수 본문과 클래스 메소드 본문을 제외한 코드들을 실행하는데 이 부분이 a.py 실행시 문제가 되어 순환 참조 에러가 발생합니다. a.py의 경우 function_a() 코드가 import time에 실행되기 때문에 a 모듈과 b 모듈이 반복적으로 import 되기 때문입니다.




해결 방법


import time이 문제가 된다는 걸 이해했다면, 순환 참조 문제는 간단하게 해결이 가능합니다. 모듈을 import 하는 시점을 import time이 아닌 runtime으로 옮기면 됩니다. a.py 의 function_a() 가 import time에 호출되기 때문에 function_a() 이후 문제가 되는 b.py 의 import a 부분을 runtime으로 옮기도록 하겠습니다.


아래의 코드처럼 해결이 가능합니다.



그 밖의 해결법


위에서 소개한 내용 이외에도 Python typing 이나 django model에서 발생하는 이슈가 있습니다. 이 경우에도 원인은 같지만 해결 방법은 조금 다릅니다.


Python typing의 경우 type hint 를 위해 기존보다 더 많은 import 가 발생하면서 순환 참조가 발생할 확률이 높아집니다. 이럴 때는 typing 모듈에서 제공하는 TYPE_CHECKING 이라는 값을 활용하면 해결할 수 있습니다.



django model 에서 발생하는 이슈의 경우에는 참조하는 모델명을 객체가 아닌 문자열로 바꾸면 해결됩니다. 문자열로 바꾸더라도 python이 객체를 찾아내 바인딩해주기 때문에 손 쉽게 해결 할 수 있습니다.




이상 Python에서 자주 발생하는 순환 참조 이슈에 대해 알아보았습니다. Python 사용에 있어서 조금이라도 도움이 되었길 바랍니다.




브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari