AI 개발

[Python] 모듈 상대경로(ImportError: attempted relative import with no known parent package)

minkyung 2021. 10. 8. 11:29

 

 

파이썬에서 모듈을 만들어서 import 하다보면 

ImportError: attempted relative import with no known parent package

위 에러를 많이 만나게 된다.

 

패키지 안에서 스크립트 파일(실행 파일)을 실행 시키려고 했을 때 발생하는 에러이다.

일단 이유는 파이썬의 인터프리터가 __main__ 의 위치를 알지 못하기 때문에 위 같은 에러가 발생하는 것이다.

파이썬의 인터프리터는 relative import의 모듈 위치를 정할 때(기준이 되는 위치) __name__ 속성에 의해 결정되고

터미널에서 파이썬을 직접 실행시키면 __name__ == '__main__'이 되기 때문이다.

 

 

이 부분이 이해가 잘 안된다면,

 

먼저 모듈과 패키지의 차이에 대해서 이해해야 한다.

모듈과 패키지는 둘다 import를 불러와 쓰는데, 패키지는 모듈을 포함시켜놓은 것이다.

 

모듈(Module) : 각종 변수, 함수, 클래스를 담고 있는 파일로 특정 기능을 .py 파일 단위로 작성한 것

패키지(Package) : 여러 모듈을 묶은 것이며 패키지는 모듈에 네임스페이스(namespace)를 제공함

 

파이썬 3.3 이하에서는 폴더가 패키지임을 명시하기 위해 __init__.py 파일을 폴더 안에 생성해야 했다. 

3.3이상에서는 __init__.py 가 없어도 패키지임을 파이썬이 인식하지만 되도록 작성해주는 것이 좋다.

 

패키지안의 모듈을 불러오는 방법은 

import pacakge.module

import package.module1, package.module2

package.module.변수

package.module.함수()

pacakge.module.클래스()

가 있다.

 

 

 

다음과 같은 구조가 있다고 할 때 main.py에서 utils 패키지의 utils 모듈(utils.py)를 가져와서 사용한다고 해보자.

__init__.py에 아무것도 쓰지 않았을 때 utils 모듈을 가져오려면 from utils import utils 처럼 패키지와 모듈을 모두 명시해주어야 한다.

 

# utils/__init__.py

from . import utils

__init__.py에 위와 같이 명시해주면 import utils만 불러와주고 utils.py안에 hello()라는 함수가 있다고 할 때 해당 함수를 바로 사용할 수 있게 된다. 패키지를 불러올 떄 (import utils) __init__.py 파일이 자동으로 실행되므로 from . 을 통해 현재 패키지에서 모듈을 가져오라고 인식할 수 있게 되는 것이다.

 

import utils

print(utils.utils.hello()) # utils 패키지의 utils 모듈(utils.py) 안의 hello() 함수 사용

 

 

이때 utils.py의 __name__ 변수에는 'utils'가 들어가 있고 utils 모듈을 불러오는 main.py의 __name__에는 '__main__'이 들어가 있게 된다. 파이썬의 경우 import 할 때 import 하는 모듈을 모두 실행하기 때문에 원하지 않는 코드가 실행될 수 있는 경우가 있다.

따라서 시작점(entry point)가 어떤 파일인지 명확히하기 위해 if __name__ == '__main__'을 통해 스크립트 파일을 실행시키는 것이다. 즉 if __name__ == '__main__' 는 현재 스크립트 파일이 프로그램의 시작점이 맞는지 판단하는 역할을 한다.

 

 


 

해결 방안

 

 

에러가 발생했을 것 같은 상황은 패키지 안에서 스크립트 파일(실행파일)을 실행했을 때이다.

예를 들어 pacakge/calculation.py에서 package/package2/add.py안의 add 함수를 불러왔을 때 entry point를 찾지 못한다.

 

# package/calcuation.py

from .package2.add import my_add

result = my_add(1,2)
print(result) # ImportError

 

 

 

1. python의 -m 옵션 이용

-m 옵션은 모듈을 스크립트로 수행할 때 쓰는 옵션인데 이를 통해 패키지 구조를 알려주면 파이썬이 상대경로를 찾을 수 있게 된다.

 

~/test/ > python3 -m package.calcuation

 

 

 

2. python의 절대경로 이용

 

# package/calcuation.py

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 package.package2 import add
	else:
		from .packge2 import add

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

 

코드를 위와 같이 작성하고 pacage/caculation.py 파일을 실행하면 된다.

 

 

 

 

 

 

References

 

https://myjorney.tistory.com/52

https://dojang.io/mod/page/view.php?id=2448

https://dodonam.tistory.com/107