ModuleNotFoundError 의 원인과 에러방지를 위해 기초를 다시 정리하고자 생각했고,
지난 글에 python __name__ 변수와 '__main__'의 의미를 정리하였다.
아래 글을 참고하자.
2022.05.24 - [coding 📟/python 💻] - [Python] __name__ 변수 __main__ 이란
이번에는 파이썬이 패키지와 모듈을 찾는 방법에대해 정리하고자 한다.
import 문이 모듈과 패키지를 찾는방법
결론부터 말하자면 파이썬이 실행되고 import를 통해 모듈과 패키지를 찾는 순서는 아래와 같다.
import --> sys.modules --> built-in 모듈 확인 --> sys.path
이런 과정을 거치고 마지막 sys.path 에서도 해당 모듈을 찾지 못하는 경우
ModuleNotFoundError 를 리턴한다.
그렇다면 의문
sys.modules 의 sys 또한 모듈인데 파이썬이 이건 어떻게 찾는가?
built-in 모듈이란, 해석 그대로 내장 모듈이다 즉 파이썬에 내장되어 있는 모듈이다.
모듈을 찾는 두번째 순서로 built-in 모듈을 확인하는데 이때 이미 내장되어 있는 sys를 발견한다.
sys.modules 와 sys.path
1. sys.modules
파이썬이 모듈이나 패키지를 찾기 위해 가장 먼저 확인하는 곳이다.
sys.modules 의 타입은 dictionary 이다.
이미 import된 모듈과 패키지들을 저장하고 있다.
한번 import된 모듈과 패키지들은 또 다시 찾지 않도록 해준다.
새로 import 하는 모듈은 찾을 수 없다.
2. sys.path
파이썬이 모듈이나 패키지를 찾기 위해 가장 마지막으로 확인하는 곳
sys.path의 타입은 string요소들을 가진 list 이다. (List['string'])
경로를 나타내는 string 요소들이다.
파이썬은 각 경로를 하나 하나 확인하면서 해당 경로에 import하고자 하는 패키지가 위치해 있는지 확인한다.
Absolute path 와 relative path
1.Absolute path 절대경로
전체구조를 살펴보자
\ProjectA
\main.py
\패키지1
\__init__.py # 해당 디렉터리가 파이썬 패키지 일부임을 알려주는 역할
\module1.py
\module2.py
\패키지2
\__init__.py # 해당 디렉터리가 파이썬 패키지 일부임을 알려주는 역할
\module3.py
\module4.py
\subpackage1
\module5.py (function2 존재)
절대 경로란 Absolute path란
import를 하는 파일이나 경로에 상관없이 항상 경로가 동일하다는 뜻이다.
위의 프로젝트에는 package1,package2가 있고 그 아래 모듈들이 있다.
만일 module5.py 에 function2 함수를 불러온다고 한다면 아래의 코드 처럼 import를 해주면된다.
from package2.subpackage1.module5 import function2
여기서 질문 경로를 입력할때 projectA 을 적지 않는 이유는 무엇일까?
위와 같이 module.py에 function2에 접근하기 위한 경로를 리눅스 directory로 표현한다면 다음과 같다.
projectA/package2/subpackage1/module5.py
파이썬에서 from import 키워드를 사용하게 되면, 이미 projectA 프로젝트에서 출발하기 때문에, projectA 적지 않는다.
2. relative path 상대경로
절대경로 처럼 최상단 디렉토리를 기준으로 경로를 잡는 것이 아닌
import 하는 현재 스크립트 위치를 기준으로 경로를 정의하는 방법이다.
package2의 module3 에서 package2의 subpackage1의 module5의 function2 함수를 import 한다면,
from .subpackage1.module5 import function2
여기서 dot(.)은 import가 선언되는 파일의 현재 위치 이다.
main 모듈에서의 import
자 이제 절대경로와 상대경로에 대해 정리해보았다.
하지만 패키지와 모듈을 통해 프로젝트를 구성하고
막상 main 코드에서 import 시 상대경로로 입력 후 실행하면 아래와 같은 원인을 만날 수 있다.
ImportError: attempted relative import with no known parnt package
> 알려지지 않는 부모 패키지에 상대경로 임포트 시도가 있었다.
main module에서 어떻게 패키지의 모듈을 임포트 해야하는가?
상대경로는 현재 모듈의 이름에 기반하고, 메인 모듈의 이름은 언제나 "__main__"이기 때문에
파이썬 어플리케이션에서 메인 모듈로 사용하려고 의도한 모듈들은 필수로 언제나 절대경로 임포트를 사용해야 한다.
왜일까?
공식문서를 참고하면
상대경로를 이용하여 import하게되면 현재 모듈의 이름을 기반으로 한다.
메인모듈의 이름은 항상 __main__이기 때문에 항상 절대경로를 사용해야한다고 적혀있다.
__main__인거랑 절대 경로를 사용하는거랑 무슨관계가 있을까?
- 상대경로를 이용하여 import 하기: __name__변수에 값으로 해당 모듈의 위치를 파악함
- 만약 __main__으로 설정 될 경우(실행하는 주 스크립트 파일로 직접 실행할 경우) 패키지의 구조가 실제 파일 시스템에 있는 위치에 관계없이 최상위 모듈인 것처럼 된다.(즉, __main__이 최상위 파일인 것처럼)
- 그렇게되면 현재 main.py는 다른 모듈의 패키지와 구분되게 된다.
- 그러므로 실행을 시키면 내가 제일 최상위 모듈 라는 의미로 "알려진 상위 패키지없이 상대 가져 오기를 시도했습니다."라는 에러로그를 띄우는 것이다.
정리하며
main 으로 할 파일에서는 절대경로를 입력해주자
왜?
__name__ 변수를 통해 모듈과 패키지를 찾는데
해당 스크립트를 main으로 실행할 시 __name__ 변수는 __main__이되기때문에 상대경로로 찾지 못한다.
그러므로 절대경로를 입력해 줌으로써 해당 에러를 방지하자.
'coding 📟 > python 💻' 카테고리의 다른 글
[Python] map 함수 (0) | 2023.08.20 |
---|---|
[Python] __name__ 변수 __main__ 이란 (0) | 2022.05.24 |
[Python] 딕셔너리 컴프리헨션 + 집합, 제너레이터 컴프리헨션 (0) | 2022.05.20 |
[Python] 리스트 컴프리헨션 List Comprension (if else) (0) | 2022.05.20 |
[Python] 가상환경 pyenv windows 윈도우 설치 사용법 (1) | 2022.05.13 |
댓글