간단한 경우를 생각해보자. 폴더 구조는 아래 사진과 같다.
import_error
라는 폴더 안에 위와 같은 구조로 파일이 존재한다. 각 파일은 아주 간단하게 작성했다.
# util_a.py
def foo():
return 1
# model_a.py
from ..util.util_a import foo
# train.py
from util.util_a import foo
util_a.py
에 있는 foo
라는 함수를 불러와서 써보는 예시다. 실행을 해보자.
$ python3 train.py
$ python3 model/model_a.py
train.py
에서는 아무 문제가 발생하지 않지만, model_a.py
를 실행하는 과정에서 아래와 같은 오류가 발생한다.
Traceback (most recent call last):
File "model/model_a.py", line 1, in <module>
from ..util.util_a import foo
ImportError: attempted relative import with no known parent package
분명 제대로 했는데 왜 오류가 날까?
먼저 원인을 아주 간략하게 알아보면, 파이썬 인터프리터가 자기보다 상위의 디렉토리를 인식할 수 없기 때문에 발생하는 문제인 듯 하다.
인터프리터가 상위 폴더를 인식 못하면, 그걸 코드로 넣어주는 방법이다.
model_a.py
를 아래와 같이 변경하면된다.
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(os.path.dirname(__file__)))
from util.util_a import foo
sys.path
를 이용해서 해당 파일의 경로를 넣어줬다. 따라서 util
폴더에 접근할 때 더이상 ..util
로 접근하는 것이 아니라, util
로 바로 접근해야만 한다.
이 방법은 해당 코드만 변경하면 깔끔하게 수정이 가능하다는 장점이 있다. 다만 code 자체만 볼 때 util.util_a
가 올바른 경로가 아니므로, 내 기준 vscode에서 Pylance가 제대로 잡아내지 못하는 문제가 존재했다. 추가적으로 경로가 한단계 상위 폴더가 아닌, 두세단계 이상 복잡해 질 경우 골치아파진다.
위에서 말했듯, 인터프리터가 상위 폴더를 인식하지 못해 발생하는 문제다. 그럼 그냥 "애초에 그걸 알게 해주면 해결 되는 것 아닌가?" 에 대한 솔루션이다.
먼저 프로젝트 최상위 디렉토리보다 한칸 더 위로 이동한다. 나의 경우 실험을 위해 위 캡쳐와 같이 import_error
라는 폴더에서 진행했다.
$ cd ~/import_error/
$ cd ../
그런 다음, 아래와 같이 우리가 평소에 python을 실행하던 방식과 약간 다르게 파일을 실행하면 문제가 해결된다. -m
으로 모듈이라는 옵션을 주고, /
대신 .
을 넣어주고, .py
는 붙이지 않고 실행한다. 말 그대로 모듈로 실행하는 것이기 때문에, 평소에 Python에서 import 하는 방식과 동일하다고 생각하면 쉽다.
# 옳은 방법
$ python3 -m import_error.model.model_a
생각해보면, model_a.py
에서 util_a.py
로 가기 위해서는 import_error
폴더 안에 어떤 파일이 있는지 알아야 한다. 즉, import_error
자체에 접근하는 과정이 필요하다. 따라서 import_error
라는 폴더에서 아래와 같이 실행하면 안되고, 꼭 위와 같은 방식으로 실행해야만 한다.
# 틀린 방법
$ python3 -m model.model_a
두가지 해결 방법을 찾았는데, 그렇게 깔끔하다는 느낌을 받지는 못해서 더 좋은 방법이 있으면 댓글 남겨주시면 감사하겠습니다.