본문 바로가기

Computer Engineering/My Stack Overflow

[파이썬 상대경로 import 에러] ImportError: attempted relative import with no known parent package

프로그래밍을 시작한 지 얼마 되지 않다보니 간단한 에러로 보이는데 기본적인 개념이 부족해서 ImportError: attempted relative import with no known parent package 에러가 발생해서 많은 시간을 소모해서 해결과정을
기록하게 됐습니다.

 


구글링을 열심히 했지만, stackoverflow 내용이 잘 이해가 되지 않았습니다. 왜 이렇게 해결이 안 되는 건지 도저히 이해가 가지 않아서

하루가 지나고 천천히 정리하다보니 조금 씩 정리가 되었습니다.

 

대부분의 자료들이 영어로 되어 있다보니 이해가 늦어서 제가 보았던 해결책들을 한글로 번역해서 자료를 만들게 됐습니다. 

 

 

ImportError: attempted relative import with no known parent package 

 

 

에러가 발생한 상황

 

 

위와 같은 디렉토리 구조를 가진 프로젝트가 있습니다.

 

package/mymath.py 에는 다음과 같은 함수가 있습니다.

def my_add(x,y):
 return x+y
 

 

package/package/mypath.py 에는

from ..mymath import my_add

a = my_add(1,2)
print(a)

다음과 같이 구현이 되어 있습니다.

 



여기서 mypath.py 를 터미널에서 실행시키기 위해 터미널의 현재위치를 cd package/package 를 통해 이동합니다.

그리고 mypath.py를 실행시키면 위와 같은 에러가 발생합니다.

 

 

 

 

 

에러가 발생한 원인

 

-> 이제 한 번 어떤 상화에서 이런 에러가 발생하는지 확인을 했으니, 에러가 발생한 원인을 찾아보도록 하겠습니다.

 

원인은 python interpreterrelative import의 module 위치를 정할 때 즉, 기준이 되는 모듈의 위치를 정할 때

__name__속성에 의해 결정됩니다. 

이미 알고 계신분들도 있고, 잊고 계신 분들도 있겠지만 터미널에서 직접 python 파일을 실행시킬 때
__name__ == '__main__' 이 됩니다. 그러면 당연히 __main__이라는 모듈의 위치를 파이썬 interpreter는 알 수가 없기 때문에 에러가 발생합니다.

 

 

 

- module 과 package 개념이 헷갈리시는 분은

 

http://myjorney.tistory.com/19 여기서 개념을 확인하시면 됩니다.



해결 방안

1. python 의 -m 옵션을 이용한다.

-m mod : run library module as a script (terminates option list)

 

-m 옵션은 모듈을 스크립트로 수행할 때 쓰는 옵션입니다.(즉, 모듈이야 라고 말해주는 거죠.)

 

1) 먼저 프로젝트 root directory인 test2 디렉토리로 이동합니다.

 

2) 그리고 python3 -m package.package.mypath 를 입력하여 실행시키면 해결됩니다.

 

 

pakage 구조를 알려줬기 때문에 python은 상대경로를 찾을 수 있습니다.

 

 

참조: https://www.napuzba.com/story/import-error-relative-no-parent/

 

 

 

2. python의 절대경로를 이용한다.

if __name__ == '__main__':
	if __package__ is None:
		import sys
		from os import path
		print(path.dirname( path.dirname( path.abspath(__file__) ) ))
		sys.path.append(path.dirname( path.dirname( path.abspath(__file__) ) ))
		from mymath import my_add
	else:
		from ..mymath import my_add

	a = my_add(1,2)
	print(a)

 

__name__ == '__main__' 은 터미널에서 스크립트 형태로 실행시켰을 때는 절대경로를 이용해서 import 해주고

 

그가 아닌 경우 상대경로를 이용합니다. print() 함수로 디렉토리 위치를 확인하면서 하면 훨씬 좋습니다.

 

 

 

 

 
정리하고 나니 마음이 너무 편하네요 :)
감사합니다!
도움이 되셨으면 좋겠습니다.