[오류 노트] ImportError: attempted relative import with no known parent package

정준환·2023년 1월 12일
0

상황


간단한 경우를 생각해보자. 폴더 구조는 아래 사진과 같다.

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

분명 제대로 했는데 왜 오류가 날까?

해결 방법


먼저 원인을 아주 간략하게 알아보면, 파이썬 인터프리터가 자기보다 상위의 디렉토리를 인식할 수 없기 때문에 발생하는 문제인 듯 하다.

1. sys.path를 이용하는 방법

인터프리터가 상위 폴더를 인식 못하면, 그걸 코드로 넣어주는 방법이다.
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가 제대로 잡아내지 못하는 문제가 존재했다. 추가적으로 경로가 한단계 상위 폴더가 아닌, 두세단계 이상 복잡해 질 경우 골치아파진다.

2. -m 옵션 (모듈) 이용하기

위에서 말했듯, 인터프리터가 상위 폴더를 인식하지 못해 발생하는 문제다. 그럼 그냥 "애초에 그걸 알게 해주면 해결 되는 것 아닌가?" 에 대한 솔루션이다.

먼저 프로젝트 최상위 디렉토리보다 한칸 더 위로 이동한다. 나의 경우 실험을 위해 위 캡쳐와 같이 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

두가지 해결 방법을 찾았는데, 그렇게 깔끔하다는 느낌을 받지는 못해서 더 좋은 방법이 있으면 댓글 남겨주시면 감사하겠습니다.

profile
정준환

0개의 댓글