TIL DAY 6 || How import statement finds modules and packages

TK·2021년 2월 21일
0

TIL

목록 보기
7/55
import pyyyython

우리가 파이썬에서 특정 모듈을 import 했다고 했을 때, 파이썬은 다음과 같은 순서로 모듈을 탐색한다.

  1. sys.module
  2. built-in modules
  3. sys.path

위의 순서로 우리가 import 하는 모듈을 탐색한다.

sys.module

  • 파이썬이 모듈이나 패키지를 찾기 위해 가장 먼저 확인하는 곳이다.
  • sys.module 은 이미 import 된 모듈과 여러 패키지들을 저장하고 있다.
  • 그렇기 때문에 sys.module 은 파이썬이 이미 한번 import 된 모듈과 패키지를 다시 재탐색 하지 않도록 해준다.
  • 그 말은 우리가 sys.module 에서 import 하지 않은 모듈을 찾을 수가 없다는 말이다.

built-in modules

  • 파이썬에서 제공하는 공식 라이브러리들을 의미한다.
  • built-in 모듈은 이미 파이썬을 다운받을 때 디폴트로 포함되어 나오므로 파이썬이 쉽게 찾을 수 있다.

sys.path

  • 마지막으로 파이썬이 탐색을 시도하는 곳이 바로 sys.path 이다.
  • list 안에 string 형식으로 여러 경로들을 담고있다.
  • list 요소들을 iterate 하면서 각 경로에 우리가 import 할 모듈이나 패키지가 있는지 탐색한다.
  • sys.path 에서도 못찾으면 ModuleNotFoundError 가 발생한다.

Python 이 sys를 import 하는 방법

  • sys 모듈은 빌트인되어 있기 때문에 built-in modules에서 찾아본다.

Absolute Path vs Relative Path

Absolute Path

  • Absolute path는 프로젝트 내에서는 어느 파일, 어느 위치에서 import 하던지 경로가 동일하다. root path는 프로젝트의 최상단 디렉토리이다.

  • 참고로 현재의 프로젝트 디렉토리는 default로 sys.path 에 포함된다. 따라서 absolute path는 현재 프로젝트 디렉토리부터 경로를 시작한다.

Relative Path

  • relative path는 프로젝트의 최상단 디렉토리를 기준으로 경로를 잡는게 아니라 import 하는 파일의 위치를 기준으로 경로를 정의한다.
  • 일반적으로 relative path는 local package 안에서 다른 local package를 import 할때 사용한다.
  • absolute path 의 경로가 길어질 수 있어서 relative path 를 사용할 수는 있지만, 경험상 파일 위치가 바뀌는 경우가 생기면 relative path 를 다시 변경해주는 것이 귀찮고 까다로움으로 absolute path 를 쓰는 것이 나아보인다.
  • / : root path
  • ./ : current path
  • ../ : 현재위치에서 상위 디렉토리로 가는 경로

calculator package directory

calculator 패키지의 경로를 위 그림과 같이 설정해보자.

  • 참고로 __init__.py 파일에는 아무코드도 없지만, 해당 디렉토리가 패키지임을 알려주는 역할을 한다.

main.py 경로설정 오류

# main.py
# absoulte path
# from calculator.add_and_multiply import add_and_multiply 


# 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

왜 이런 오류가 떴는지 공식 문서를 참조해보자.
파이썬 공식문서 중 PEP 328 에서 말하는 내용은 다음과 같다.

Relative imports use a module's name attribute to determine that module's position in the package hierarchy. If the module's name does not contain any package information (e.g. it is set to 'main') then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.

다음을 정리해보면,

  • relative pathimport 는 현재 모듈의 이름 에 기반한다.

  • 여기서 말하는 모듈의 이름이란 모듈의 __name__ 속성 값을 의미한다.

  • 만약 이 모듈의 __name__ 값이 아무런 패키지 정보를 포함하고 있지 않으면 relative import 는 이 모듈이 어디에 위치하고 있던간 최상위에 있는 모듈로 인식한다.

  • 파이썬 인터프리터는 단지 모듈명이 __main__ 이라는 정보만으로는 이 모듈이 어느 위치에 있는지 알 수가 없다.

  • 따라서 에러가 발생하는 것이다.

  • 제일 처음 실행하는 모듈은 메인 모듈이 되며, 메인모듈__name__ 은 항상 __main__ 이다.

따라서

  • 파이썬에서 메인 모듈로 사용하려고 의도한 모듈들은 필수적으로 절대경로를 이용한 import 를 해야한다.
  • 또는, python 의 -m 옵션을 이용하여, 해당 파일이 모듈임을 알려준 뒤 실행한다.
# main.py
# absoulte path
from calculator.add_and_multiply import add_and_multiply 


if __name__ == '__main__':
    print(add_and_multiply(1,2))

위와 같이 절대경로를 이용해 import 해줄 경우, 오류 없이 실행된다.

add_and_multiply.py 경로설정 오류

# add_and_multiply.py
from .multiplication import multiply
# from root_calculator.multiplication import multiply


def add_and_multiply(a,b):
    return multiply(a,b) + (a+b)

해당 파일 역시 다음과 같이 import error 가 난다.

ImportError: attempted relative import with no known parent package

이유는 위에서 설명한 것과 같이 relative import 를 하는 경우, 직접 실행하는 파일의 __name____main__ 이기 때문에 패키지 경로를 찾을 수 없어서 에러가 난다.

마찬가지로 absoulte path 로 변경해주면 오류없이 import 를 수행한다.

__init__.py 의 역할

python3.3 이전 :

__init__.py 파일은 해당 디렉터리가 패키지의 일부임을 알려주는 역할을 한다. 만약 위 calculator 패키지의 add_and_multiply.py 가 속해있는 디렉터리에 __init__.py 파일이 없다면 해당 폴더가 패키지로 인식되지 않는다.

python3.3 이후 :

__init__.py 파일이 없어도 패키지로 인식하지만 하위 버전 호환을 위해 __init__.py 파일을 생성하는 것이 안전하다.

profile
Backend Developer

0개의 댓글