Python) How import statement finds modules & packages

solseye·2020년 7월 7일
0

Python

목록 보기
4/4

Python이 module과 package를 검색하는 방법

python이 module/package를 찾는 순서

sys.modules → built-in modules → sys.path

  1. sys.modules
    python이 module이나 package를 찾기 위해 가장 먼저 확인하는 곳입니다. sys.modules는 딕셔너리 자료형이며, 이미 import된 module과 package를 저장하고 있습니다. 따라서 한 번 import된 module과 package는 python이 다시 찾지 않아도 됩니다.

  2. built-in modules
    python 공식 라이브러리로 이미 python에 포함되어 나오기 때문에 쉽게 찾을 수 있습니다.

  3. sys.path
    python이 마지막으로 확인하는 곳이며, stirng element를 갖고 있는 list 자료형입니다.

python은 list의 각 경로들을 하나씩 확인하면서 해당 경로에 import 하고자 하는 package가 있는지 확인합니다.

sys module을 import 해서 sys.modules와 sys.path를 출력하고 수정할 수 있습니다.

import sys

print(sys.path)
print(sys.modules)

sys.path에서도 찾지 못하면 ModuleNotFoundError를 리턴합니다.

Absolute Path / Relative Path

파이썬의 built-in 모듈과 pip을 통해 설치한 외부 모듈 및 패키지는 일반적으로 import하는데 문제가 발생하지 않습니다.

built-in 모듈은 원활하게 검색되고, pip으로 설치한 외부 모듈도 자동으로 site-packages라는 디렉토리에 설치됩니다. (sys.path에 포함됩니다.)

그러나, 직접 개발한 local package를 import할 때는 해당 패키지의 위치에 맞게 import 경로를 잘 선언해주어야 합니다.

local package를 import하는 경로는 absoulte pathrelative path가 있습니다.

absolute path(절대 경로)

절대 경로는 import 하는 파일이나 경로에 상관없이 항상 경로가 동일합니다.
예를 들어서 살펴보도록 하겠습니다.

└── my_app
    ├── main.py
    ├── package1
    │   ├── module1.py
    │   └── module2.py
    └── package2
        ├── __init__.py
        ├── module3.py
        ├── module4.py
        └── subpackage1
            └── module5.py

위의 tree 구조를 보면, my_app이라는 프로젝트에 package1과 package2가 포함되어 있습니다.
또한 package2는 subpackage1이라는 중첩 package를 갖고 있습니다.

절대 경로를 사용해서 package1과 package2를 import하면, 다음과 같습니다.

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 => 리눅스의 디렉토리 경로 형식

my_app\package2\subpackage\module5.py => 윈도우 형식

my_app.package2.subpackage1.module5.py => 파이썬 경로 형식

my_app 프로젝트 내에서는 어느 파일, 어느 위치에서 import 하던지 경로가 항상 동일하므로 절대 경로입니다.

current directory는 default로 sys.path에 포함됩니다. 따라서 절대 경로는 current directory부터 시작합니다.

local package를 import 할 때는 절대 경로를 사용합니다.

단, 절대 경로를 사용할 시, 경로가 길어질 수 있다는 단점이 있습니다.

relative path(상대 경로)

상대 경로는 import하는 위치를 기준으로 경로를 정의합니다.
주로 local package 안에서 다른 local package를 import 할 때 사용합니다.

위의 my_app 프로젝트 예시로 다시 돌아가서,
package2의 module3에서 package2의 class1과 package2의 하위 package인 subpackage1의 module5의 function2 함수를 import하면, 다음과 같습니다.

# package2/module3.py

from . import class1
from .subpackage1.module5 import function2

참고
.(dot) : import가 선언되는 파일의 현재 위치
..(dot) : 현재 위치에서 상위 디렉토리로 가는 경로

상대 경로는 선언해야 하는 경로의 길이를 줄여준다는 장점이 있습니다. 하지만 경로를 헷갈리기 쉽고, 파일 위치가 변경되면 경로 위치도 변경되어야 합니다.
그러므로 absolute path 사용을 권장하고 있습니다.

Questions

앞에서 살펴 본 내용을 토대로 몇 가지 문제들을 풀어보도록 하겠습니다.

Q1) sys.modules와 sys.path의 차이점

파이썬이 모듈/패키지를 찾는 순서에서 가장 처음이 sys.module, 가장 마지막이 sys.path입니다.

sys.modules : 모듈 이름을 이미 로드된 모듈로 매핑하는 딕셔너리로, 모듈의 재로딩과 기타 트릭을 강제하기 위해 조작 가능
sys.path : 파이썬 모듈들이 저장되어 있는 위치로, 모듈의 검색 경로를 지정하는 문자열 리스트

Q2) Python이 sys module의 위치를 찾는 방법

sys는 python에 내장된 모듈이므로 python을 설치할 때 기본적으로 내장 모듈에 대한 path가 기본으로 지정되어 있습니다.

Q3) calculator package 실습

Q4) main module에서는 package의 module을 import하는 방법

main.py에서 상대경로로 add_and_multipy를 import하면 ImportError: attempted relative import with no known parent package 에러가 발생합니다.

따라서, main module에서는 절대 경로로 import해야만 합니다.
즉, 메인 모듈로 사용될 목적의 모듈들은 반드시 절대 경로로 import합니다.

Q5) main module을 절대 경로와 상대 경로로 import할 때의 차이점

터미널에서 main.py에 상대 경로를 import한 뒤, python main.py를 실행해보면,

Traceback (most recent call last):
  File "main.py", line 5, in <module>
    from .calculator.add_and_multiply import add_and_multiply
ImportError: attempted relative import with no known parent package

라는 에러가 발생합니다.

왜 이러한 에러가 발생하는 것일까요?
그 이유는 바로 main module이기 때문입니다. main module로 사용되는 모듈은 반드시 절대 경로로 import 해야만 에러가 발생하지 않습니다.

Q6) __init__.py 파일의 역할

__init__ .py는 해당 디렉토리가 패키지의 일부임을 알려주는 역할을 합니다.
특정 디렉토리의 모듈을 '*'을 사용하여 import할 때, 해당 디렉토리의 __init__.py 파일에 __all__ 변수를 설정하고 import할 수 있는 모듈을 정의해주어야 합니다.

0개의 댓글