[Python] How import statement finds modules and packages

바울·2022년 6월 27일
0

sys.modules 와 sys.path의 차이점

sys.modules: 파이썬 인터프리터가 제공하는 변수와 함수를 직접 제어할 수 있게 해주는 모듈이다. 단순한 dictionary 입니다. 그리고 이미 import된 모듈과 package들을 저장하고 있습니다. 즉, 한번 import된 모듈과package들은 파이썬이 또 다시 찾지 않아도 되도록 하는 기능을 가지고 있습니다. 그러므로 새로 import 하는 모듈은 sys.modules 에서 찾을 수 없다.

sys.path: 파이썬 모듈들이 저장되어 있는 위치를 나타낸다. 즉 이 위치에 있는 파이썬 모듈은 경로에 상관없이 어디에서나 불러올 수 있다. sys.path는 기본적으로 list이며 string요소 들을 가지고 있다. 파이썬이 sys.path에서도 모듈이나 패키지를 못 찾으면 ModuleNotFoundError를 리턴한다.

>>> import sys

>>> sys.path

['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages']

>>> sys.path.append("/root/mymod")

>>> sys.path

['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages', '/root/mymod']

sys 도 import 해야하는 모듈입니다. 파이썬이 sys modules의 위치를 찾는방법

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

  1. sys.modules

  2. built-in modules

  3. sys.path

sys.modules

위에 설명한 것과 같다. sys.modules을 보고 파일이 없으면 built-in modules로 넘어간다.

built-in modules

파이썬에서 제공하는 파이썬 공식 라이브러리들 입니다. Built-in 모듈들은 이미 파이썬에 포함되어 나오므로 파이썬이 쉽게 찾을 수 있습니다. 만약 built-in modules에도 없을경우 sys.path로 넘어간다.

sys.path

위에 설명한 것과 똑같이 마지막으로 sys.path에서 파일을 찾는다. 참고로 sys 는 파이썬에 포함되어 있는 모듈입니다. 그러므로 다음 처럼 sys 모듈을 import 해서 sys.modules와 sys.path 를 출력할수도 있고 수정 할 수 도 있다.

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

Absolute path와 relative path의 차이점

Local package를 import 하는 경로에는 absolute path 와 relative path 가 있습니다.

Absolute path는 절대적인, 파일이 가지고 있는 고유한 경로를 사용한다.
Relative path는 상대적인, 즉 지금 위치한 곳을 기준해서 사용한다.

Relative path는 선언해야 하는 경로의 길이를 줄여준다는 장점은 있지만 헷갈리기 쉽고 파일 위치가 변경되면 경로 위치도 변경되어야 하는 단점이 있습니다. 그러므로 웬만한 경우 absolute path를 사용하는게 권장 됩니다.

main.py에서 상대경로로 add_and_mutiply 를 임포트 했을 때 발생하는 에러가 발생한다, main module 에서는 패키지의 모듈을 어떻게 임포트 해야할까?

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)

임포트시 에러발생

Traceback (most recent call last):
  File "/Users/jacob/Library/Mobile Documents/com~apple~CloudDocs/wecode_pre_study/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"이기 때문에 파이썬 어플리케이션에서 메인 모듈로 사용하려고 의도한 모듈들은 필수로 언제나 절대경로 임포트를 사용해야 한다.

add_and_multiply.py에서 multiply함수를 절대경로와 상대경로도 각각 임포트 해보고 main 모듈과 차이점을 생각해보고 결과를 출력해보기

절대경로와 상대경로를 모두 임포트해본 결과 절대경로는 정상적으로 임포트 되었지만 상대경로는 임포트되지 않았다.
add_and_multiply.py의 부모 경로가 multiplication.py가 아니라 오류가 발생한 것을 볼 수 있다.
main 모듈을 기본 모듈로 사용하기 위해서는 상대경로를 사용해서는 프로그램을 돌릴 수 없는 거 같다.

__init__.py 파일의 역할

__init__.py 파일은 해당 디렉터리가 패키지의 일부임을 알려주는 역할을 한다. 만약 game, sound, graphic 등 패키지에 포함된 디렉터리에 __init__.py 파일이 없다면 패키지로 인식되지 않는다.

※ python3.3 버전부터는 __init__.py 파일이 없어도 패키지로 인식한다(PEP 420). 하지만 하위 버전 호환을 위해 __init__.py 파일을 생성하는 것이 안전한 방법이다.

0개의 댓글