[TIL - 5 / Python] How Import Statement Finds Modules & Packages

haejun-kim·2020년 7월 22일
0

[Python]

목록 보기
11/19
post-custom-banner

코딩을 할 때, 필요에 의해서 module과 package를 만들고 그렇게 생성한 module과 package를 import해서 변수,함수,클래스 등을 사용한다. 그렇다면 파이썬은 module과 package를 어떻게 찾는것인지 알아보자. 앞으로 코딩할 때 각종 라이브러리, 패키지 등을 import하는 경우가 아주 많으므로 해당 내용을 알고있으면 많이 도움이 될 것이다.

Import Search 순서

파이썬은 다음의 3가지 장소를 순서대로 보면서 찾는다.

  • sys.modules
  • built-in modules
  • sys.path

sys.modules

파이썬이 module이나 package를 찾기위해 가장 먼저 확인하는 곳이다. sys.modules는 단순한 dictionary이며, 이미 import 된 module과 package들을 저장하고 있다. 그렇기 때문에 한번 import 한 module이나 package는 파이썬이 다시 찾지 않아도 되도록 하는 기능을 갖는다.

built-in modules

파이썬에서 제공하는 파이썬 공식 라이브러리들이다. Built-in modules는 이미 파이썬 파일에 포함되어있기 때문에 파이썬이 쉽게 찾을 수 있다.

sys.path

마지막으로 확인하는 장소가 sys.path이다. sys.pathstring 요소들을 가지고 있는 list이다. 각 string요소들은 다음처럼 경로를 나타낸다.

['/run_dir', 
'/usr/local/lib/python38.zip', 
'/usr/local/lib/python3.8', 
'/usr/local/lib/python3.8/lib-dynload', '/home/runner/.local/share/virtualenvs/python3/lib/python3.8/site-packages']

위와 같이 sys.path 혹은 sys.modules를 확인하고 싶다면 sys 파이썬에 포함되어 있는 모듈이기 때문에 import시켜서 확인할 수 있다.

import sys

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

정리

파이썬은 import 하고자 하는 모듈과 package를 찾을때에 먼저 sys.modules를 보고, 없으면 파이썬 built-in 모듈들을 확인 하고 마지막으로 sys.path에 지정되어 있는 경로들을 확인해서 찾는다.
마지막 sys.path 에서도 못찾으면?
ModuleNotFoundError 에러를 리턴한다.


절대 경로 & 상대 경로

파이썬의 built-in modulespip로 설처한 라이브러리들은 파이썬이 경로를 찾는데 문제가 없기 때문에 import하는데에 있어 문제가 발생하지 않는다. (설치한 라이브러리들은 sys.path가 포함하고 있는 site-packages에 저장되기 때문에 파이썬이 쉽게 찾는다.) 하지만 경로에 대해서 이해하고 넘어가야하는 이유는 직접 개발한 local package때문이다. 직접 개발한 local package는 파이썬에서 경로를 검색할 수 있도록 직접 지정해주어야하는데 여기서 알아야하는 개념이 바로 경로에 대한 개념이다.

절대 경로 (Absolute Path)

import하는 파일이나 경로에 상관없이 항상 경로가 동일

위의 그림으로 이해해보자. my_app이라는 프로젝트가 있고 프로젝트에는 package1, package2라는 두개의 패키지를 가지고 있다. 각 패키지는 파이썬 파일을 가지고 있고 package2subpackage1 라는 패키지도 포함하고 있다. 절대경로를 사용해서 package1package2import해보면 다음과 같이 할 수 있다.

from package1 import module1
from package1.module2 import function1
from package2 import class1

경로들의 시작점이 프로젝트의 가장 최상단 경로인 my_app에서 시작하는것을 확인할 수 있다.

만약 module5.pyfunction2 함수를 import하고싶다면,

from package2.subpackage1.module5 import function2

이렇게 절대 경로를 주어 import시켜줄 수 있다.

이러한 경로 표현 방식은 OS에 따라 다르게 표현되는데

  • 리눅스
    my_app/package2/subpackage1/module5.py

  • 윈도우
    my_app\package2\subpackage1\module5.py

  • 파이썬
    my_app.package2.subpackage1.module5.py

위에 사용한 예시에서는 이미 my_app의 위치이므로 my_app은 생략되고, 다음과 같이 표현된다.
package2.subpackage1.module5.py

해당 내용을 from ~ import ~를 사용하면 다음과 같이 표현된다.

from package2.subpackage1.module5 import function2

절대경로는 어느 위치에서 import를 하던 경로가 항상 동일하다. 일반적으로 보통 절대 경로를 사용하지만 절대 경로를 사용한다면 경로가 길어질 수 있다는 단점이 존재한다. 그래서 이러한 단점을 보완하기 위해서 사용되는것이 상대 경로(Relative Path)이다.

상대 경로 (Relative Path)

상대 경로는 절대 경로와는 다르게 현재 위치를 기준으로 하여 경로를 정의한다. 때문에 일반적으로 상대 경로는 local package 에서 다른 local package를 import할 때 주로 사용된다. 다음과 같은 예를 들 수 있다.

# package2/module3.py <- 현재 디렉토리의 위치

from . import class1
from .subpackage1.module5 import function2

여기서 from 뒤에 사용된 .은 현재 위치를 표현할 때 사용된다. 현재위치는 package2/module3.py 이므로 현재 위치에서부터 원하는 모듈의 경로만 선언해주면 된다.

( ..을 사용해서 현재 위치에서 상위 디렉토리로 위치를 이동할 수 있다. )

상대 경로는 선언해야 하는 경로의 길이를 줄여준다는 장점은 있지만 헷갈리기 쉽고 파일 위치가 변경되면 경로 위치도 변경되어야 하는 단점이 있다.


Assingment

1. sys.modules 와 sys.path의 차이점

sys.moduels

  1. 가장 먼저 확인한다.
  2. dictionary 구조이다.
  3. import한 모듈이나 패키지를 다시 찾지 않아도 된다.

sys.path

  1. 마지막으로 확인한다.
  2. string 요소들을 가지고 있는 list 구조이다.

2. 파이썬은 sys 모듈의 위치를 어떻게 찾을 수 있을까?

위 문제의 답을 찾기 위해서 실제로 sys.modules를 출력해보자.

import sys

print(sys.modules)

출력 결과 중 다음과 같은 부분을 확인할 수 있을것이다.

'sys': <module 'sys' (built-in)>

dictionary형태로 sys라는 key값이 이미 저장되어 있다. sys 모듈은 이미 built-in돼있기 때문에 built-in module이 있는 부분에서 찾게 된다.

3. Absolute path와 relative path의 차이점

  • Absolute path
    절대경로는 import하는 파일이나 경로에 상관없이 항상 프로젝트의 최 상단 경로로부터 시작하며, 경로가 동일하다. 경로가 길어질 수 있는 단점은 있지만 경로를 수정해 줄 필요가 현저히 적고 경로를 파악하는데도 훨씬 명확하기 때문에 주로 사용된다.

  • Relative path
    상대경로는 현재 위치를 기준으로 경로를 정의한다. 현재 위치는 .으로 표현되며, 프로젝트의 최상단이 아닌 현재 위치를 기준으로 하기때문에 작성하기 편리하고, 경로의 작성이 짧아진다는 장점이 있지만 파일 위치가 변경되면 경로 위치 또한 수정해줘야 하는 유지보수의 어려움이 존재한다.

4. calculator 패키지 만들기

위와 같은 경로를 가진 calculator package를 직접 만들어보자.

  • 패키지 구성

  • main.py

  • add_and_multiply.py

  • multiplication.py

5-1. main.py에서 상대경로로 add_and_mutiply 를 임포트 했을 때 발생하는 에러를 확인

위에서 작성한 코드를 실행시켜보면 다음과 같은 에러가 발생한다.

부모 패키지가 정의되어있지 않은 상태에서 상대 경로로 import를 시도해서 발생한 import error다.

5-2. 다음의 파이썬 공식 문서를 참고해서 main module 에서는 패키지의 모듈을 어떻게 임포트 해야하는지

파이썬 공식 문서

  • 절대경로로 import 경로를 정의해줄 것.

  • 파이썬 공식문서를 참고해보면 다음과 같이 권고하는데,
    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.
    즉, 상대 경로 import는 현재 모듈의 이름을 기반으로한다. 기본 모듈의 이름은 항상 __main__이므로 Python 응용 프로그램의 기본 모듈로 사용하려는 모듈은 항상 절대 가져 오기를 사용해야한다. 이제 위의 내용들을 참고하여 절대 경로로 변경하여 오류를 해결 후 결과를 출력해보자.

  • 오류 해결

  • 출력 결과

위와 같이 정상 동작한다.

6-1. add_and_multiply.py에서 multiply함수를 절대경로와 상대경로도 각각 임포트 해볼 것

  • 절대 경로
from calculator.multiplication import multiply
def add_and_multiply(a,b):
    return multiply(a,b) + (a+b)
  • 상대 경로
from .multiplication import multiply
def add_and_multiply(a,b):
    return multiply(a,b) + (a+b)
  • 실행 결과

실행 결과는 동일하다.

6-2. main 모듈과 차이점을 생각 및 결과 출력

add_and_multiply.py__main__.py가 아니기 때문에 절대 경로든 상대 경로든 문제 없이 동작한다. __main__.py가 아니면 절대 경로든 상대 경로든 작동에는 문제가 없다.

7. __init__.py 파일의 역할

  • __init__.py가 존재하는 디렉토리는 패키지의 일부임을 알려주는 역할을 한다.
  • package 초기 설정을 할 수 있는 역할을 한다.
  1. __init__.py 파일이 존재하지 않는다면 패키지의 일부인지 인식을 하지 못하기 때문에 속성을 가지고 있지 않는다는 에러가 발생하게 된다.
  2. 현재는 __init__.py파일은 빈 파일이지만 package 실행 시 설정해주고 싶은 초기 설정이 있다면 이 파일에 초기 설정에 관련된 코드들을 작성해준다. 그러면 작성 한 코드의 내용대로 package 실행 시 자동으로 실행된다.
post-custom-banner

0개의 댓글