How import statement works

Posted by Afsal on 01-Mar-2024

Hi Pythonistas!

In this post we are learning how import works in Python. These are the steps involved in importing

Module Name Resolution:

When you try to import a module (e.g., import module_name), Python needs to determine where the module is located.

The module name is first checked against built-in modules.If not found, the sys.path list is examined. This list contains directories where Python searches for modules.

>>> import sys

>>> sys.path

['', '/usr/lib/python310.zip', '/usr/lib/python3.10', '/usr/lib/python3.10/lib-dynload', '/usr/local/lib/python3.10/dist-packages', '/usr/lib/python3/dist-packages']

Module Loading:

Once the module is located, Python checks if it has already been loaded. If it has, Python uses the existing module. If the module is not loaded, Python proceeds with the loading process.

>>> 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)>, 'posix': <module 'posix' (built-in)>, '_frozen_importlib_external': <module '_frozen_importlib_external' (frozen)>, 'time': <module 'time' (built-in)>, 'zipimport': <module 'zipimport' (frozen)>, '_codecs': <module '_codecs' (built-in)>, 'codecs': <module 'codecs' from '/usr/lib/python3.10/codecs.py'>, 'encodings.aliases': <module 'encodings.aliases' from '/usr/lib/python3.10/encodings/aliases.py'>, 'encodings': <module 'encodings' from '/usr/lib/python3.10/encodings/__init__.py'>, 'encodings.utf_8': <module 'encodings.utf_8' from '/usr/lib/python3.10/encodings/utf_8.py'>, '_signal': <module '_signal' (built-in)>, '_abc': <module '_abc' (built-in)>, 'abc': <module 'abc' from '/usr/lib/python3.10/abc.py'>, 'io': <module 'io' from '/usr/lib/python3.10/io.py'>, '__main__': <module '__main__' (built-in)>, '_stat': <module '_stat' (built-in)>, 'stat': <module 'stat' from '/usr/lib/python3.10/stat.py'>, '_collections_abc': <module '_collections_abc' from '/usr/lib/python3.10/_collections_abc.py'>, 'genericpath': <module 'genericpath' from '/usr/lib/python3.10/genericpath.py'>, 'posixpath': <module 'posixpath' from '/usr/lib/python3.10/posixpath.py'>, 'os.path': <module 'posixpath' from '/usr/lib/python3.10/posixpath.py'>, 'os': <module 'os' from '/usr/lib/python3.10/os.py'>, '_sitebuiltins': <module '_sitebuiltins' from '/usr/lib/python3.10/_sitebuiltins.py'>, 'apport_python_hook': <module 'apport_python_hook' from '/usr/lib/python3/dist-packages/apport_python_hook.py'>, 'sitecustomize': <module 'sitecustomize' from '/usr/lib/python3.10/sitecustomize.py'>, 'site': <module 'site' from '/usr/lib/python3.10/site.py'>, 'readline': <module 'readline' from '/usr/lib/python3.10/lib-dynload/readline.cpython-310-x86_64-linux-gnu.so'>, 'atexit': <module 'atexit' (built-in)>, '_ast': <module '_ast' (built-in)>, 'itertools': <module 'itertools' (built-in)>, 'keyword': <module 'keyword' from '/usr/lib/python3.10/keyword.py'>, '_operator': <module '_operator' (built-in)>, 'operator': <module 'operator' from '/usr/lib/python3.10/operator.py'>, 'reprlib': <module 'reprlib' from '/usr/lib/python3.10/reprlib.py'>, '_collections': <module '_collections' (built-in)>, 'collections': <module 'collections' from '/usr/lib/python3.10/collections/__init__.py'>, 'types': <module 'types' from '/usr/lib/python3.10/types.py'>, '_functools': <module '_functools' (built-in)>, 'functools': <module 'functools' from '/usr/lib/python3.10/functools.py'>, 'contextlib': <module 'contextlib' from '/usr/lib/python3.10/contextlib.py'>, 'enum': <module 'enum' from '/usr/lib/python3.10/enum.py'>, 'ast': <module 'ast' from '/usr/lib/python3.10/ast.py'>, '_opcode': <module '_opcode' from '/usr/lib/python3.10/lib-dynload/_opcode.cpython-310-x86_64-linux-gnu.so'>, 'opcode': <module 'opcode' from '/usr/lib/python3.10/opcode.py'>, 'dis': <module 'dis' from '/usr/lib/python3.10/dis.py'>, 'collections.abc': <module 'collections.abc' from '/usr/lib/python3.10/collections/abc.py'>, 'importlib._bootstrap': <module '_frozen_importlib' (frozen)>, 'importlib._bootstrap_external': <module '_frozen_importlib_external' (frozen)>, 'warnings': <module 'warnings' from '/usr/lib/python3.10/warnings.py'>, 'importlib': <module 'importlib' from '/usr/lib/python3.10/importlib/__init__.py'>, 'importlib.machinery': <module 'importlib.machinery' from '/usr/lib/python3.10/importlib/machinery.py'>, '_sre': <module '_sre' (built-in)>, 'sre_constants': <module 'sre_constants' from '/usr/lib/python3.10/sre_constants.py'>, 'sre_parse': <module 'sre_parse' from '/usr/lib/python3.10/sre_parse.py'>, 'sre_compile': <module 'sre_compile' from '/usr/lib/python3.10/sre_compile.py'>, '_locale': <module '_locale' (built-in)>, 'copyreg': <module 'copyreg' from '/usr/lib/python3.10/copyreg.py'>, 're': <module 're' from '/usr/lib/python3.10/re.py'>, 'token': <module 'token' from '/usr/lib/python3.10/token.py'>, 'tokenize': <module 'tokenize' from '/usr/lib/python3.10/tokenize.py'>, 'linecache': <module 'linecache' from '/usr/lib/python3.10/linecache.py'>, 'inspect': <module 'inspect' from '/usr/lib/python3.10/inspect.py'>, 'rlcompleter': <module 'rlcompleter' from '/usr/lib/python3.10/rlcompleter.py'>}

Module Compilation (if needed):

If the module is a Python source file (.py), it needs to be compiled into bytecode (.pyc).

This step only occurs if the compiled bytecode is not present or if the source file is newer than the bytecode.

Namespace Creation:

A namespace for the module is created, typically represented by a dictionary.

The module's code is executed within this namespace.

Module Execution:

The module's code is executed line by line, and the namespace is populated with the variables, functions, and classes defined in the module.

Namespace Binding:

The module's namespace is bound to a name in the current namespace. For example, if you import module_name, you can access its contents using module_name.some_function().

Circular Import Checks:

Python checks for circular imports to avoid infinite loops. If a circular import is detected, modules may not be fully initialized until the import chain is resolved.

Finalization:

The imported module is now available for use in the current script or module.

This is only the high-level  overview of import working. We can make a python script that emulates this behavior in an upcoming post.

I hope you have learned something from this post. Please share you valuable suggestions with afsal@parseltongue.co.in