모듈과 패키지를 import하기 위해서는 어디에 위치해있는지 파이썬이 찾을 수 있어야 한다. 파이썬은 다음 3가지 장소를 순서대로 보면서 찾는다.
파이썬이 가장 먼저 확인하는 곳이다.
sys.modules는 dictionary로 이미 import된 모듈과 패키지들을 저장하고 있다. 즉, 한번 import된 모듈과 패키지들은 파이썬이 또 다시 찾지 않아도 되도록 한다. 그러므로 새로 import 하는 모듈은 sys.modules에서 찾을 수 없다.
파이썬에서 제공하는 파이썬 공식 라이브러리들이다.
마지막으로 보는 장소가 바로 sys.path이다.
sys.path는 기본적으로 list로 각 요소는 string 형태로 경로를 나타낸다. 파이썬은 list의 각 경로를 하나 하나 확인하면서 해당 경로에 import 하고자 하는 패키지가 위치해 있는지 확인한다. (참고로 sys는 파이썬에 포함되어 있는 모듈이다.)
파이썬은 import하고자 하는 모듈을 sys.path에서도 못찾으면 ModuleNotFoundError
에러를 리턴한다.
sys
는 build-in module로 파이썬이 설치된 위치에 같이 저장되어 있다.
Built-in 모듈은 당연히 잘 찾아지고, pip 으로 설치한 외무 모듈도 자동으로 site-packages
라는 디렉토리에 설치가 되는데, 이 site-packages
는 sys.path
에 이미 포함되어 있기 때문에 찾는데 문제가 없다.
문제는 직접 개발한 local package이다. 직접 개발한 local package를 import 할때는 해당 package의 위치에 맞게 import 경로를 잘 선언해야 한다. Local package를 import 하는 경로에는 absolute path 와 relative path 가 있다.
Absolute path
는 절대 경로로 import하는 파일이나 경로에 상관없이 항상 경로가 동일하다.
└── my_app
├── main.py
├── package1
│ ├── module1.py
│ └── module2.py
└── package2
├── __init__.py
├── module3.py
├── module4.py
└── subpackage1
└── module5.py
my_app이라는 프로젝트는 package1과 package2라는 2개의 package를 가지고 있다. 그리고 package2는 subpackage2 라는 중첩 package를 가지고 있다.
from package1 import module1
from package1.module2 import function1
from package2 import class1
from package2.subpackage1.module5 import function2
경로들의 시작점이 전부 "my_app" 프로젝트의 가장 최상위 디렉토리에서 시작한다.
예를 들어, subpackage1의 module5 모듈의 function2 함수를 import 하기 위해서는 다음 경로를 거친다.
my_app => package2 => subpackage1 => module5.py
my_app/package2/subpackage1/module5.py # 리눅스의 directory 경로 형식
my_app\package2\subpackage1\module5.py # 윈도우즈 형식
my_app.package2.subpackage1.module5.py # 파이썬 형식
이미 my_app 프로젝트 안에 있으므로 my_app 은 생략한다
package2.subpackage1.module5.py
Relative Path
는 최상단 디렉토리를 기준으로 경로를 잡는게 아니라 import 하는 위치를 기준으로 경로를 정의한다. 그래서 일반적으로 relative path
는 local package 안에서 다른 local package를 import 할때 사용한다.
# package2/module3.py
from . import class1
from .subpackage1.module5 import function2
여기서 dot(.)은 import가 선언되는 파일의 현재 위치이다. 현재위치는 package2/module3.py 이므로 현재 위치에서부터 원하는 모듈의 경로만 선언해주면 된다.
# subpackage1/module5.py
from ..module4 import class4
dot 2개(..) 는 현재위치에서 상위 디렉토리로 가는 경로이다.
absolute path
는 선언해야 하는 경로가 길어질 수 있다.
Relative path
는 경로의 길이를 줄여준다. 하지만 헷갈리기 쉽고 파일 위치가 변경되면 경로 위치도 변경되어야 하는 단점이 있다.
그러므로 웬만한 경우 absolute path
를 사용하는게 권장된다.
디렉토리 구조
__init__.py
__init__.py에 아무코드도 없지만 해당 디렉토리가 패키지임을 알려주는 역할을 한다.
main.py
add_and_multiply.py
multiplication.py
main.py에서 상대경로로 add_and_mutiply를 import 시도하니
# relative path
from .calculator.add_and_multiply import add_and_multiply
if __name__ == '__main__':
print(add_and_multiply(1,2))
ImportError: attempted relative import with no known parent package
에러가 발생한다.
파이썬공식문서를 통해 어떤 문제인지 파악해보자.
Note that relative imports are based on the name of the current module. Since the name of the main module is always
__main__
, modules intended for use as the main module of a Python application must always use absolute imports.
해석하면
relative imports는 현재 모듈의 이름을 기반으로 한다. 메인 모듈의 이름은 항상
__main__
이기 때문에, 파이썬 어플리케이션의 메인 모듈로 사용하려는 모듈은 항상 absolute imports를 사용해야한다.
여기에서 이해가 안되는 부분은 main module이 무엇을 의미하는가이다. main.py
에 main이 들어갔다고 main module은 아닐 것이고..
공식문서를 다시 살펴보니 main module을 다음과 같이 정의해놓았다.
the collection of variables that you have access to in a script executed at the top level and in calculator mode
해석하면
메인 모듈은 최상위 수준에서 실행되는 스크립트나 계산기 모드(인터프리터 모드)에서 액세스하는 변수들의 컬렉션이다.
쉽게 설명하면 최상위 수준에서 실행되는 스크립트란 처음 실행하는 소스 파일을 의미한다. (최상위 모듈이라고도 한다.) 그 파일(모듈)이 프로그램 실행의 시작점이 된다. 그리고 스크립트가 아닌 인터프리터에서는 무엇을 전달하든, 그것이 메인 모듈이 된다. 이름이 무엇이든 상관없다. 참고1 참고2
반대로 어떤 모듈이 다른 곳에 import되어 사용된다면 메인 모듈이 아닌 것이다.
여기서main.py
는 메인 모듈이므로 상대경로가 아닌 절대경로로 import해야 한다!
# absoulte path
from calculator.add_and_multiply import add_and_multiply
if __name__ == '__main__':
print(add_and_multiply(1,2))
5 # 출력
참고로 여기서 if __name__ == '__main__':
은 메인 모듈인지 식별하기 위한 조건문이다. 해당 모듈이 메인 모듈일 때 __name__
이 __main__
이 되고 아래의 코드를 실행한다. 해당 모듈이 다른 모듈로 import되었을 때는 코드가 실행되지 않는다. 참고사이트-코딩도장
2가지 케이스로 나눠서 생각해볼 수 있겠다. 첫 번째, add_and_multiply.py이 메인 모듈일 경우, 다시 말해 그 파일에서 바로 실행했을 경우이다. 두 번째, add_and_multiply.py이 메인 모듈이 아닐 경우, 다시 말해 다른 파일(main.py)이 add_and_multiply.py을 import했을 경우이다.
# relative path
from .multiplication import multiply
-> ImportError: attempted relative import with no known parent package
에러 발생
# absoulte path
from calculator.multiplication import multiply
-> ModuleNotFoundError: No module named 'calculator'
에러 발생
# absoulte path
from multiplication import multiply
-> 정상동작
여기서 add_and_multiply.py
는 메인 모듈이므로 상대경로가 아닌 절대경로로 import해야 한다. 이미 calculator 패키지 안에 있으므로 calculator는 경로에서 생락한다.
Case2. add_and_multiply.py이 메인 모듈이 아닐 경우
main.py이 다음과 같이 add_and_multiply.py을 import했다.
# main.py
from calculator.add_and_multiply import add_and_multiply
if __name__ == '__main__':
print(add_and_multiply(1,2))
이때는 add_and_multiply.py의 import문이 절대 경로이든 상대 경로이든 상관없다.
# add_and_multiply.py
# absoulte path
from calculator.multiplication import multiply
def add_and_multiply(a,b):
return multiply(a,b) + (a+b)
또는
# add_and_multiply.py
# relative path
from .multiplication import multiply
def add_and_multiply(a,b):
return multiply(a,b) + (a+b)
그런데 절대 경로일 때 from multiplication import multiply
로 하면 오류가 난다. 왜 calculator
까지 적어줘야 하는지는 모르겠다..
참고사이트
https://docs.python.org/3/tutorial/modules.html#intra-package-references
https://wikidocs.net/84414
https://dojang.io/mod/page/view.php?id=2448
https://python.bakyeono.net/chapter-10-1.html
https://velog.io/@choonghee-lee/WeCode-How-Import-Statement-Finds-Modules-Packages
https://realpython.com/absolute-vs-relative-python-imports/