Python - How Import Statement Finds Modules & Packages

ryan·2020년 8월 25일
0
post-thumbnail

1. sys.modules와 sys.path의 차이점을 서술해 주세요.

sys.modules

system과 관련된 정보를 가지고 있는 모듈

sys.modules는 파이썬 런타임 환경의 다른 부분들을 제어하는데 사용하는 함수들과 변수들을 제공합니다. 파이썬은 sys.modules에서 제일먼저 모듈이나 패키지를 찾고, sys.modules는 딕셔너리 구조이며 import 되어있는 모듈과 패키지를 저장합니다. 즉, 다시 찾지 않아도 됩니다.

# practice.py
import sys

print(sys.argv)		# argv: arguments value(명령 매개 변수)


# Terminal
python practice.py	# 입력
['practice.py']		# 출력됨

python practice.py 1 2 3 4 5	# 입력
['practice.py', '1', '2', '3', '4', '5']	# 출력됨
# 명령을 입력할 때, 지정하는 매개변수들의 값이 들어온다고 해서 '명령 매개 변수'라고 한다.

sys.path

sys.path는 파이썬 모듈들이 저장되어 있는 위치를 나타냅니다. 즉, 이 위치에 있는 파이썬 모듈은 경로에 상관없이 어디에서나 불러올 수 있죠. 파이썬은 모듈과 패키지를 첫번 째, 두번째로 찾고 마지막으로 sys.path를 찾습니다. sys.path는 string 요소로 된 리스트 구조이고, 처음의 리스트 요소부터 마지막까지 찾습니다. 파이썬에 포함되어 있는 것은 built-in modules이고, sys.path에서도 모듈을 발견하지 못하면 ModuleNotFoundError를 에러로 리턴합니다.

>>> import sys
>>> sys.path
['', '/home/realryankim/miniconda3/lib/python38.zip', '/home/realryankim/miniconda3/lib/python3.8', '/home/realryankim/miniconda3/lib/python3.8/lib-dynload', '/home/realryankim/.local/lib/python3.8/site-packages', '/home/realryankim/miniconda3/lib/python3.8/site-packages']

sys.modules vs sys.path

sys.modules sys.path
모듈, 패키지 찾는 순서 가장 먼저! 마지막에!
타입 딕셔너리 {모듈이름: 모듈경로} string 요소들을 가진 리스트
역할 이미 import된 모듈과 패키지들을 저장하고, 한번 import된 모듈과 패키지들은 또 다시 찾지 않도록 해준다. string 요소들로 경로를 나타내준다.
특징 새로 import하는 모듈은 찾을 수 없다. 파이썬은 각 경로를 순차적으로 확인하면서 해당 경로에 import하고자 하는 패키지가 위치해 있는지 확인한다.

2. sys 도 import 해야하는 모듈입니다. 파이썬은 sys 모듈의 위치를 어떻게 찾을 수 있을까요?

built-in modules

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

sys 모듈은 이미 built-in 되어 있기 때문에 built-in module들이 있는 부분에서 찾을 수 있습니다.

>>> import sys
>>> print(sys.modules)
{'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, '_frozen_importlib': <module '_frozen_importlib' (frozen)>, '_imp': <module '_imp' (built-in)>, '_warnings': <module '_warnings' (built-in)>, '_frozen_importlib_external': <module '_frozen_importlib_external' (frozen)>, '_io': <module 'io' (built-in)>, 'marshal': <module 'marshal' (built-in)>, 'posix': <module 'posix' (built-in)>, '_thread': <module '_thread' (built-in)>, '_weakref': <module '_weakref' (built-in)>, 'time': <module 'time' (built-in)>, 'zipimport': <module 'zipimport' (frozen)>, '_codecs': <module '_codecs' (built-in)>, 'codecs': <module 'codecs' from '/home/realryankim/miniconda3/lib/python3.8/codecs.py'>, 'encodings.aliases': <module 'encodings.aliases' from '/home/realryankim/miniconda3/lib/python3.8/encodings/aliases.py'>, 'encodings': <module 'encodings' from '/home/realryankim/miniconda3/lib/python3.8/encodings/__init__.py'>, 'encodings.utf_8': <module 'encodings.utf_8' from '/home/realryankim/miniconda3/lib/python3.8/encodings/utf_8.py'>, '_signal': <module '_signal' (built-in)>, '__main__': <module '__main__' (built-in)>, 'encodings.latin_1': <module 'encodings.latin_1' from '/home/realryankim/miniconda3/lib/python3.8/encodings/latin_1.py'>, '_abc': <module '_abc' (built-in)>, 'abc': <module 'abc' from '/home/realryankim/miniconda3/lib/python3.8/abc.py'>, 'io': <module 'io' from '/home/realryankim/miniconda3/lib/python3.8/io.py'>, '_stat': <module '_stat' (built-in)>, 'stat': <module 'stat' from '/home/realryankim/miniconda3/lib/python3.8/stat.py'>, '_collections_abc': <module '_collections_abc' from '/home/realryankim/miniconda3/lib/python3.8/_collections_abc.py'>, 'genericpath': <module 'genericpath' from '/home/realryankim/miniconda3/lib/python3.8/genericpath.py'>, 'posixpath': <module 'posixpath' from '/home/realryankim/miniconda3/lib/python3.8/posixpath.py'>, 'os.path': <module 'posixpath' from '/home/realryankim/miniconda3/lib/python3.8/posixpath.py'>, 'os': <module 'os' from '/home/realryankim/miniconda3/lib/python3.8/os.py'>, '_sitebuiltins': <module '_sitebuiltins' from '/home/realryankim/miniconda3/lib/python3.8/_sitebuiltins.py'>, 'site': <module 'site' from '/home/realryankim/miniconda3/lib/python3.8/site.py'>, 'readline': <module 'readline' from '/home/realryankim/miniconda3/lib/python3.8/lib-dynload/readline.cpython-38-x86_64-linux-gnu.so'>, 'atexit': <module 'atexit' (built-in)>, 'rlcompleter': <module 'rlcompleter' from '/home/realryankim/miniconda3/lib/python3.8/rlcompleter.py'>}

3. Absolute path와 relative path의 차이점을 서술해 주세요.

Absolute Path

최상단 디렉토리를 기준으로 경로를 설정합니다.

/home/users/c/computerhope/public_html/cgi-bin
# 웹 주소
https://www.computerhope.com/oh.htm

relative path

현재의 프로젝트 실행 디렉토리를 기준으로 경로를 설정합니다.

/public_html/cgi-bin
# 웹 주소
oh.htm

4. calculator 패키지 만들기

5. main.py에서 상대경로로 add_and_mutiply 를 임포트 했을 때 발생하는 에러를 확인하고, 다음의 파이썬 공식 문서를 참고해서 main module 에서는 패키지의 모듈을 어떻게 임포트 해야하는지 블로깅 해주세요.

https://docs.python.org/3/tutorial/modules.html#intra-package-references

(base) realryankim@realryankim:~/project/test$ /home/realryankim/miniconda3/bin/python /home/realryankim/project/test/main.py
Traceback (most recent call last):
  File "/home/realryankim/project/test/main.py", line 5, in <module>
    from .calculator.add_and_multiply import add_and_multiply
ImportError: attempted relative import with no known parent package
# import에러: 시도했어. relative import를, 무엇으로? 알려지지 않은 부모 패키지로
  • main.py에서 from .calcalator로 시작하는데 '.'은 상대 경로(relative path, 현재 디렉토리)를 지정하고, calculator는 최상위 패키지입니다. 그래서 에러가 발생하고, 이 때는 절대 경로(absolute path, 완전함, Root 디렉토리에서부터 시작, 현재 위치와 상관 없이 항상 정확히 해당 경로로 이동 가능)를 지정해 줌으로써 에러를 해결할 수 있습니다.
  • 참고로 '..'은 현재 디렉토리 바로 전 디렉토리를 지정한다.


그럼 에러가 해결됩니다.

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

  • add_and_multiply.py는 calculator 패키지 모듈 중 하나입니다.
  • main.py는 add_and_multiply.py 모듈을 받아서 실행시키는 파일입니다.

절대 경로로 출력

# absolute path
from multiplication import multiply

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

print(add_and_multiply(2, 3))

# 결과값
8

상대 경로로 출력

# relative path
from .multiplication import multiply

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

print(add_and_multiply(2, 2))

# 에러 메세지
Traceback (most recent call last):
  File "/home/realryankim/project/test/calculator/add_and_multiply.py", line 2, in <module>
    from .multiplication import multiply
ImportError: attempted relative import with no known parent package

상대경로로 import 할 때, main.py에서 발생한 것과 같은 에러가 뜹니다. 마찬가지로 main module인 add_and_multiply 모듈의 위치를 잡지 못해 발생하는 에러입니다.

7. init.py 파일의 역할에 대해서도 정리해서 블로깅 해주세요.

  • init.py 파일은 파이썬이 그 파일을 포함하는 디렉토리를 모듈로 여기도록 하는 파일이다.
  • init.py은 모듈에서 첫번째로 읽혀지는 파일로 모듈이 읽혀질 때마다 실행하려는 코드를 실행하거나 내보낸 하위 모듈을 지정하는 데 사용할 수 있습니다
profile
👨🏻‍💻☕️ 🎹🎵 🐰🎶 🛫📷

0개의 댓글