Python 의 import 탐색 순서(Searching flow)에 대해 알아보겠습니다

math.factorial(5)

내장 module인 math.py 에 정의 되어있는 factorial 함수를 사용하고자 합니다
import 과정이 어떻게 진행하는지 알아보겠습니다


import 과정의 깊은 내용(바이트코드 레벨) 링크

https://docs.python.org/3/tutorial/modules.html#the-module-search-path


내부에서는 어떻게 동작하고 있을까요?

>>> import math
>>> math.factorial(5) 
120

import 후 module에 있는 내용에 접근 할때마다
"module을 매번 컴파일(complie) 해서 사용하는 걸까요?"


Import Searching Flow

If the named module is not found in sys.modules, then Python’s import protocol is invoked to find and load the module. This protocol consists of two conceptual objects, finders and loaders. A finder’s job is to determine whether it can find the named module using whatever strategy it knows about. Objects that implement both of these interfaces are referred to as importers - they return themselves when they find that they can load the requested module.

Python includes a number of default finders and importers. The first one knows how to locate built-in modules, and the second knows how to locate frozen modules. A third default finder searches an import path for modules. The import path is a list of locations that may name file system paths or zip files. It can also be extended to search for any locatable resource, such as those identified by URLs.

The import machinery is extensible, so new finders can be added to extend the range and scope of module searching.

https://docs.python.org/3/reference/import.html#finders-and-loaders
Python Docs

Python의 import system의 과정(import 탐색 순서(Searching flow))를 도식화 해보면 다음과 같습니다

  • import 하고자 하는 module이
    1. sys.modules에 존재하는지 확인합니다
    2. 없다면 내장 module에 존재하는지 확인합니다
    3. 없다면 sys.path에 존재하는지 확인합니다
    4. 없다면 ModuleNotFoundError가 발생됩니다
    1, 2, 3 과정 중에서 import 한 module이 존재한다면 import 가 성공하고 이후 단계는 생략합니다

참고로 sys는 System-specific parameters and functions 의 약자 입니다
https://docs.python.org/3/library/sys.html#module-sys

>>> import sys
>>> sys.modules
{'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, '_frozen_importlib': <module '_frozen_importlib' (frozen)>, '_imp': <module '_imp' (built-in)>, '_thread': <module '_thread' (built-in)>, '_warnings': <module '_warnings' (built-in)>, '_weakref': <module '_weakref' (built-in)>, '_io': <
module '_io' (built-in)>, 'marshal': <module 'marshal' (built-in)>, 'nt': <module 'nt' (built-in)>, 'winreg': <module 'winreg' (built-in)>, '_frozen_importlib_external': <module '_frozen_importlib_external' (frozen)>, 'time': <module 'time' (built-in)>, 'zipimport': <module 'zipimport' (frozen)>, '_codecs': <modul
e '_codecs' (built-in)>, 'codecs': <module 'codecs' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\codecs.py'>, 'encodings.aliases': <module 'encodings.aliases' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\encodings\\aliases.py'>, 'encodings': <module
 'encodings' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\encodings\\__init__.py'>, 'encodings.utf_8': <module 'encodings.utf_8' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\encodings\\utf_8.py'>, '_codecs_kr': <module '_codecs_kr' (built-in)>, '_mu
ltibytecodec': <module '_multibytecodec' (built-in)>, 'encodings.cp949': <module 'encodings.cp949' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\encodings\\cp949.py'>, '_signal': <module '_signal' (built-in)>, '_abc': <module '_abc' (built-in)>, 'abc': <module 'abc' from 'C:\\Users\
\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\abc.py'>, 'io': <module 'io' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\io.py'>, '__main__': <module '__main__' (built-in)>, '_stat': <module '_stat' (built-in)>, 'stat': <module 'stat' from 'C:\\Users\\jigiseong\\AppD
ata\\Local\\Programs\\Python\\Python310\\lib\\stat.py'>, '_collections_abc': <module '_collections_abc' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\_collections_abc.py'>, 'genericpath': <module 'genericpath' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\
lib\\genericpath.py'>, 'ntpath': <module 'ntpath' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\ntpath.py'>, 'os.path': <module 'ntpath' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\ntpath.py'>, 'os': <module 'os' from 'C:\\Users\\jigiseong\\AppData\
\Local\\Programs\\Python\\Python310\\lib\\os.py'>, '_sitebuiltins': <module '_sitebuiltins' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\_sitebuiltins.py'>, 'itertools': <module 'itertools' (built-in)>, 'keyword': <module 'keyword' from 'C:\\Users\\jigiseong\\AppData\\Local\\Progra
ms\\Python\\Python310\\lib\\keyword.py'>, '_operator': <module '_operator' (built-in)>, 'operator': <module 'operator' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\operator.py'>, 'reprlib': <module 'reprlib' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\l
ib\\reprlib.py'>, '_collections': <module '_collections' (built-in)>, 'collections': <module 'collections' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\collections\\__init__.py'>, 'types': <module 'types' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\
\types.py'>, '_functools': <module '_functools' (built-in)>, 'functools': <module 'functools' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\functools.py'>, 'importlib._bootstrap': <module '_frozen_importlib' (frozen)>, 'importlib._bootstrap_external': <module '_frozen_importlib_exte
rnal' (frozen)>, 'warnings': <module 'warnings' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\warnings.py'>, 'importlib': <module 'importlib' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\__init__.py'>, 'importlib.machinery': <module 'impor
tlib.machinery' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\machinery.py'>, 'importlib._abc': <module 'importlib._abc' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\_abc.py'>, 'collections.abc': <module 'collections.abc' from '
C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\collections\\abc.py'>, 'contextlib': <module 'contextlib' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\contextlib.py'>, 'enum': <module 'enum' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Pytho
n310\\lib\\enum.py'>, '_sre': <module '_sre' (built-in)>, 'sre_constants': <module 'sre_constants' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\sre_constants.py'>, 'sre_parse': <module 'sre_parse' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\sre_par
se.py'>, 'sre_compile': <module 'sre_compile' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\sre_compile.py'>, '_locale': <module '_locale' (built-in)>, 'copyreg': <module 'copyreg' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\copyreg.py'>, 're': <mod
ule 're' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\re.py'>, 'typing.io': <class 'typing.io'>, 'typing.re': <class 'typing.re'>, 'typing': <module 'typing' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\typing.py'>, 'importlib.abc': <module 'importl
ib.abc' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\abc.py'>, 'importlib.util': <module 'importlib.util' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\util.py'>, '_virtualenv': <module '_virtualenv' from 'C:\\Users\\jigiseong\\
PycharmProjects\\velog\\venv\\lib\\site-packages\\_virtualenv.py'>, 'site': <module 'site' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\site.py'>, 'atexit': <module 'atexit' (built-in)>, 'math': <module 'math' (built-in)>}
>>> sys.path
['', 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\python310.zip', 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\DLLs', 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib', 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310', 'C:\\Use
rs\\jigiseong\\PycharmProjects\\velog\\venv', 'C:\\Users\\jigiseong\\PycharmProjects\\velog\\venv\\lib\\site-packages']

sys.modules와 sys.path가 채택한 자료구조

  • sys.modules

    • sys.modules 은 dictionary 객체 입니다.
      • dictionary는 Mapping type 이므로 key 값으로 hashable 객체를 받습니다
        즉, 조회(Read) 시 key 값으로 빠르게 조회할 수 있습니다.
      • dictionary는 mutable type 이므로 동일 객체로써 수정이 가능합니다
  • sys.path

    • sys.path 는 list 객체 입니다.
      • list는 mutable Sequence type 이므로 index가 특징이였습니다.
        즉, 조회 시 index에 따라 순차적으로 조회합니다.
        - 특이점으로는 0번째 index의 요소(element)는 현재 시작점을 의미합니다 (= project directoty)

        "The current working directory – denoted by an empty string – is handled slightly differently from other entries on sys.path."

        https://docs.python.org/3/reference/import.html#path-entry-finders
        Python Docs

      • list는 mutable type 이므로 동일 객체로써 수정이 가능합니다

list, dictionary 링크


Cache

공식 문서(Python Docs)에서 확인해보면

This mapping serves as a cache of all modules that have been previously imported, including the intermediate paths.
https://docs.python.org/3/reference/import.html#searching

"한 번 import 했던 module 은 sys.modules에 cache" 한다고 합니다.

"열심히 도식화 해봤는데 이해가 잘 가시나요? ㅎㅎ"

"정리해보자면"
import 하기위해 컴파일된 파일을 sys.modules 에 cache해 두었다가
다시 import 를 시도한다면 새로 컴파일 하는 것이 아니라
sys.modules 에 cache 해둔 컴파일한 파일에 접근합니다


프로그램의 속도와 효율

import 후 module에 있는 내용에 접근 할때마다
"module을 매번 컴파일 해서 사용하는 걸까요?"

"당연하게도 아니였습니다!"

"자주 쓰이는 module의 컴파일 내용은 cache 처리와 dictionary의 특징을 살려 프로그램의 처리 속도를 올리고자 함이 팍팍 느껴지지 않나요 ㅎㅎ?"


의문점은 import math 후 import sys를 했는데 sys.modules에 'math' key가 생긴 것을 확인 할 수 있다.

아직 미해결


print() 와 같이 import 없이 사용 가능한 것은 어떤 logic 인걸까?
아직 미해결


그런데 sys 또한 내장 module 입니다

math.py 와 마찬가지로 sys.py 도 내장 module 입니다

  • 따라서 같은 logic으로
    1. import 탐색 과정에 의해 2번째 단계에서 import 된 후
    2. sys.modules에 cache 됩니다

내장 module 확인 방법

sys.builtin_module_names

그런데 내장 module이 어떤 것이 있는지 궁금하지 않으신가요?

"sys는 System-specific parameters and functions 약자입니다. 따라서 system 자체가 갖다 쓸것 같은 변수명이 저장되어있을것 같다는 생각이 들지 않으셨나요? ㅎㅎ"

sys.py의 builtin_module_names 변수를 통해 확인할 수 있습니다

GUI 환경에서 확인해 보겠습니다


pycharm

따라서 명령어로 확인해 보겠습니다.

>>> import sys
>>> sys.builtin_module_names
('_abc', '_ast', '_bisect', '_blake2', '_codecs', '_codecs_cn', '_codecs_hk', '_codecs_iso2022', '_codecs_jp', '_codecs_kr', '_codecs_tw', '_collections', '_contextvars
', '_csv', '_datetime', '_functools', '_heapq', '_imp', '_io', '_json', '_locale', '_lsprof', '_md5', '_multibytecodec', '_opcode', '_operator', '_pickle', '_random', '
_sha1', '_sha256', '_sha3', '_sha512', '_signal', '_sre', '_stat', '_statistics', '_string', '_struct', '_symtable', '_thread', '_tracemalloc', '_warnings', '_weakref',
 '_winapi', '_xxsubinterpreters', 'array', 'atexit', 'audioop', 'binascii', 'builtins', 'cmath', 'errno', 'faulthandler', 'gc', 'itertools', 'marshal', 'math', 'mmap', 
'msvcrt', 'nt', 'sys', 'time', 'winreg', 'xxsubtype', 'zlib')

---

사용 중인 interpreter의 내장 module들을 확인 할 수 있습니다.


내장 module이 아닌, 사용자가 따로 만드는 local module 및 local package 는 어떻게 import 될까요?

수동

물론 sys.modules나 sys.path 에 직접 경로를 Update 해도 import 가능합니다

자동

project directory를 생성하면 해당 directory의 절대경로가 sys.path에 자동으로 추가됩니다.
project 위치와 site-packages 라는 정보가 저장됩니다.

따라서 import 시, import 탐색 순서 3번째에 단계에서 import에 성공하게 됩니다

내가 만든 local directory

velog 라는 project directory를 생성하였기에, sys.path에 자동으로 추가된 절대경로를 확인 할 수 있습니다

>>> sys.path
['', ... 중략 ...,
'C:\\Users\\jigiseong\\PycharmProjects\\velog\\venv', 'C:\\Users\\jigiseong\\PycharmProjects\\velog\\venv\\lib\\site-packages']

내장 module과 마찬가지로 local module 를 실행해보겠습니다

import sys
from calculator.add_and_multiply import add_and_multiply

if __name__ == '__main__':
    print(add_and_multiply(1,2))
    print(sys.modules)

또한 한 번의 import 후 sys.modules에 cache 되어있음을 확인 할 수 있습니다

C:\Users\jigiseong\PycharmProjects\velog\venv\Scripts\python.exe C:/Users/jigiseong/PycharmProjects/velog/main.py
5
{ ...중략..., '_virtualenv': <module '_virtualenv' from 'C:\\Users\\jigiseong\\PycharmProjects\\velog\\venv\\lib\\site-packages\\_virtualenv.py'>, 'site': <module 'site' from 'C:\\Users\\jigiseong\\AppData\\Local\\Programs\\Python\\Python310\\lib\\site.py'>, 'calculator': <module 'calculator' from 'C:\\Users\\jigiseong\\PycharmProjects\\velog\\calculator\\__init__.py'>, 'calculator.multiplication': <module 'calculator.multiplication' from 'C:\\Users\\jigiseong\\PycharmProjects\\velog\\calculator\\multiplication.py'>, 'calculator.add_and_multiply': <module 'calculator.add_and_multiply' from 'C:\\Users\\jigiseong\\PycharmProjects\\velog\\calculator\\add_and_multiply.py'>}

Process finished with exit code 0
profile
궁금증 주도 공부 / 원리 파고들기 / 경험에 기반한 블로그

0개의 댓글