TIL 15 | Import Module & Package

임종성·2021년 7월 13일
0

Python

목록 보기
4/7
post-thumbnail

sys

Module과 Package를 사용하기 위해 Import하는 경우 Python은 어떻게 Module과 Package를 찾을까? 예를 들어 abc라는 패키지가 존재한다면, 이를 사용하기 위해 다음과 같이 import한다.

import abc

이 경우 python은 abc라는 패키지가 존재하는 경로를 찾아야 import가 가능하다.

Python은 다음의 3가지 장소를 순서대로 보며 찾는다.

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

sys.modules

sys.modules는 단순한 dictionary로 이미 import된 모듈이나 패키지를 저장하고 있어 다시 찾지 않아도 되도록 하는 기능을 하고 있다. 따라서 새로 import하는 모듈은 sys.modules에서 찾을 수 없다.

built-in modules

파이썬이 제공하는 공식 라이브러리들로, built-in module들은 이미 파이썬에 포함되어 있어 쉽게 찾을 수 있다.

sys.path

sys.path는 기본적으로 string 요소들을 가지고 있는 list 이다. 파이썬은 list의 각 경로를 하나하나 확인하며 해당경로에 import 하고자 하는 모듈이나 패키지가 존재하는지 확인한다.

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

sys.modules와 sys.path의 차이점

대체로 각각의 특징을 위에서 살펴보았지만, 중요한 차이텍스트

  • sys.modules은 자료구조가 dictionary이지만 sys.pathstring 요소를 가지는 list이다.
  • import하는 모듈이나 패키지의 경로를 찾을 경우 sys.modules를 가장 먼저 보고, sys.path는 가장 나중에 확인한다.

sys의 경로

sys 또한 import해야하는 module이고, 파이썬에 내장되어 있는 built-in module이다. 따라서 파이썬 설치시 기본적으로 경로가 저장되어 있으므로 쉽게 찾을 수 있다.

Absolute Path and Relative Path

파이썬의 built-in module은 경로가 저장되어 있어 import에 문제가 없고, 외부 모듈 및 패키지는 sys.path에 이미 포함되어 있는 site-packages라는 디렉토리에 설치되기에 문제가 없다.

그러나 직접 개발한 local package를 import할때에는 해당 package 위치에 맞게 경로를 잘 지정해야 한다. 경로를 지정할 경우 absolute pathrelative path 두 가지 방법이 존재한다.

Absolute path는 프로젝트 폴더 최상위 기준으로 경로가 표시되며, relative path는 import하는 파일 기준으로 경로가 표시된다.

Absolute path는 어느 파일, 어느 위치에서 import하던지 경로가 동일하다. 따라서 파일 위치가 변경되는 경로 위치를 변경할 필요가 없다. 그러나 경로 길이가 길어질 수 있다는 단점이 존재한다.

Relative path는 absolute path와 다르게 프로젝트 최상단 디렉토리 기준이 아니라 import하는 위치를 기준으로 한다고 했으므로 absolute path에 비해 짧은 경로 길이를 가질 수 있어 일반적으로 local package 안의 local package를 import할 경우 자주 사용된다. 경로 길이가 짧은 것이 장점이지만 파일의 변경이 있을 경우 경로 위치도 변경되어야 하는 단점이 있다.

Caculator Package 만들기

Module과 Package를 Import하는 과정을 이해하기 위해 아래와 같은 디렉토리 구조와 코드를 가지는 패키지를 만드는 실습을 해보았다.

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))

add_and_multiply.py

from .multiplication import multiply
# from calculator.multiplication import multiply

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

multiplication.py

def multiply(a,b):
    return(a*b)

main.py

위와 같은 패키지를 직접 작성하여 main.py에서 relative path로 add_and_multiply.py를 import하였더니 다음과 같은 에러가 발생하였다.

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

Error의 의미를 보면 알려진 parent package가 없다는 것이다. 참고자료를 확인해보니 상대경로를 지정하는 경우 현재 위치 모듈의 이름을 따르는데, main.py와 같은 메인 모듈의 경우 모듈 이름이 __main__으로 지정되기에 메인 모듈은 상대경로를 쓸 수 없고 반드시 절대경로를 사용해야 한다는 내용이었다.

따라서 코드를 다음과 같이 바꾸고 실행하였더니 에러가 사라지고 정상적인 결과가 출력되었다.


# 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))

# output : 5

add_and_multiply.py

다음으로 add_and_multiply.py에서 multiply function을 상대경로로 import 해봤고 다음과 같은 에러가 나왔다.

from .multiplication import multiply
#from calculator.multiplication import multiply

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

print(add_and_multiply(1,2))


Traceback (most recent call last):
  File "/Users/imjongseong/Desktop/python1/calculator/add_and_multiply.py", line 1, in <module>
    from .multiplication import multiply
ImportError: attempted relative import with no known parent package

마치 main.py에서 상대경로로 import할때와 같은 결과가 나왔다. 이번에는 절대경로로 import 해봤더니 또 에러가 나왔다.

#from .multiplication import multiply
from calculator.multiplication import multiply

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

print(add_and_multiply(1,2))

Traceback (most recent call last):
  File "/Users/imjongseong/Desktop/python1/calculator/add_and_multiply.py", line 2, in <module>
    from calculator.multiplication import multiply
ModuleNotFoundError: No module named 'calculator'

이번에는 calculator라는 패키지가 존재하지 않는것처럼 결과가 출력되었다. 분명히 add_and_multiply.pycalculator 디렉토리 안에 존재하는데, 어떻게 된 일일까? 곰곰히 생각해보니 add_and_multiply.pymain.py 같이 메인 모듈 행세를 하고 있는 것 같은 느낌이 들어 main.py의 경우처럼 절대경로로 import 했더니 정상 출력이 되었다.

from multiplication import multiply
#from calculator.multiplication import multiply

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

print(add_and_multiply(1,2))

# output : 5

분명히 main.py에서 add_and_multiply function을 import 했을때 정상적으로 작동한 것을 봤는데 실제로 add_and_multiply.py에서는 절대경로, 상대경로 둘 다 에러가 발생한 것을 볼 수 있었다.

곰곰히 생각해보니 main.py에서 import할 경우는 모듈로써 실행한 것이지 직접 파일로 실행한 것이 아니었다. 하지만 add_and_multipy.py에서 실행한 경우 모듈로써 실행한 것이 아니라 직접 실행했기에 메인 모듈 취급을 받았다. 그렇기에 자기가 속한 calculator 패키지가 존재하지 않는 것처럼 행동하고, 상대경로 또한 사용할 수 없던 것이다.

init

init.py는 자신이 위치한 디렉토리가 패키지의 일부임을 확인해주는 역할을 한다. python의 버전 3.3부터는 init.py 파일이 없어도 패키지를 인식하도록 업데이트되었다고 한다. 하지만 하위 호환을 위해 init.py를 생성하는 것이 안전하다.

profile
어디를 가든 마음을 다해 가자

0개의 댓글