파이썬에서는 import
를 활용해 현재 작성하고 있는 파일 바깥의 패키지 및 모듈을 불러와 그 안에 있는 요소들을 가져다 쓸 수 있는데요, 이때 파이썬이 어떻게 패키지와 모듈이 있는 곳을 찾아나서는지 정리해 보고자 합니다.
Module이란 변수나 함수, 클래스 등을 모아놓은 파일로, 다른 파일에서 재 사용이 가능하게 하기 위한 목적도 있고, 전체 코드가 너무 커져 여러 파일로 나누고자 하는 니즈가 있을 때 사용하기도 합니다.
Package란 module과 마찬가지로 다른 파일에서 불러와 사용하는 개념인데, 여러 모듈들을 하나의 디렉토리로 모아놓고 이를 package라 부릅니다.
import sample
sample이란 이름의 package가 있다고 해봅시다. 이 sample package를 사용하려면 일단 파이썬은 sample이 위치한 곳을 찾아내야합니다. 이때 파이썬은 다음의 3 가지 장소를 순서대로 돌아다니며 sample을 찾아나섭니다.
sys.modules
는 파이썬이 module이나 package를 찾기 위해 가장 먼저 둘러보는 곳으로, 이미 import된 module과 package들을 dictionary 형태로 저장하고 있습니다. 즉, 이미 import된 친구들은 파이썬이 다시 찾는 수고를 하지 않게 만들어주며, 새로 import할 친구들은 여기서 찾을 수 없습니다.
import sys
>>> print(sys.modules)
{'sys': <module 'sys' (built-in)>,
'builtins': <module 'builtins' (built-in)>,
'_frozen_importlib': <module 'importlib._bootstrap' (frozen)>,
'_imp': <module '_imp' (built-in)>, ...... }
built-in modules
는 파이썬에서 제공하는 공식 라이브러리들 입니다. built-in modules는 이미 파이썬에 포함되어 있어 금방 찾아냅니다.
마지막으로 파이썬은 sys.path
에서 module과 package를 찾습니다. sys.path는 파이썬 라이브러리들이 설치되어있는 경로를 보여주며, string을 요소로 갖고 있는 리스트로 이루어져 있습니다. 각각 아래와 같이 나타납니다.
>>> import sys
>>> print(sys.path)
['/Users/LeeJongMin/Desktop', '/Users/LeeJongMin/Documents', '/Library/Frameworks/Python.framework/Versions/3.8/lib/python38.zip', ....]
마지막 순서인 sys.path
에서도 찾지 못하면 파이썬은 ModuleNotFoundError
를 반환합니다.
sys
자체도 모듈이기 때문에 import해서 사용해야하는데, 이때도 역시 위와 같은 순서로sys
를 찾아가 import합니다.sys.modules
와sys.path
모두 sys 모듈 안에 있지만 전자는 이미 설치된 module/package를 보여주고 후자는 라이브러리가 설치된 경로를 보여준다는 차이점이 있습니다.
하지만 위의 순서를 따라 파이썬이 모두 뒤져도 찾을 수 없는 module/package가 있을 수 있습니다. 바로 직접 개발한 local package 입니다. 이런 경우 직접 import 경로를 입력해줘야 하는데요, 입력 방식에는 두 가지가 있습니다.
절대 경로는 말 그대로 import를 하는 파일이나 경로에 상관 없이 항상 동일한 경로를 작성합니다.
project > package1 > module1.py
라는 경로에 module이 위치하고 있다면, 아래와 같이 절대 경로를 사용해 import 할 수 있습니다. 프로젝트의 가장 최상위 디렉토리에서 경로가 시작합니다.
from package1.module1 import function1
참고로 current directory 라고 하는 현재의 프로젝트 디렉토리는 default로 sys.path 에 포함되게 됩니다. 그러므로 absolute path는 current directory 로 부터 경로를 시작하게 되는것입니다.
절대 경로를 사용하게 되면 경로가 길어진다는 단점이 있는데, 이를 보완하기 위한 방법이 상대 경로 입니다.
상대 경로는 현재 import를 하는 위치를 기준으로 경로를 정의합니다. 그래서 일반적으로 상대 경로는 local package 안에서 다른 local package를 import 할 때 사용합니다.(다른 곳에 있으면 경로를 줄이고자 상대 경로를 쓰는 의미가 없음)
예를 들어 package1의 module1에서 package1의 class1을 import 하고자 한다면 아래와 같이 할 수 있습니다.
# package1/module1.py
from . import class1
dot(.)은 import가 선언된 파일의 현재 위치를 이야기하고, 현재 위치는 package1/module1.py 이므로 현재 위치에서 원하는 모듈의 경로만 정의해주면 됩니다.
상대 경로가 절대 경로에 비해 길이를 짧게 해준다는 장점은 있지만, 헷갈리기 쉽고 파일 위치가 변경되면 경로 위치가 변경되어야 하는 단점이 있어 웬만하면 절대 경로 사용을 권장합니다.