Система імпорту в Python – це механізм, що дозволяє організовувати код у модулі та пакети, повторно використовувати функції й класи, а також підключати сторонні бібліотеки. У цьому посібнику ми розберемо, як працює імпорт, які його типи існують та правила його використання.

Модулі та пакети

  • Модуль (Module) – це будь-який Python-файл з розширенням .py. Назва модуля відповідає назві файлу без розширення.
  • Пакет (Package) – це директорія, що містить спеціальний файл __init__.py (він може бути порожнім) та інші модулі або підпакети. Починаючи з Python 3.3+, існує поняття namespace package – директорія без __init__.py, яка все одно може працювати як пакет. Але у практичній розробці файл __init__.py зазвичай залишають, щоб явно позначити пакет і додати ініціалізаційний код при імпорті.

Приклад структури проєкту:

my_project/
├── main.py
└── my_package/
    ├── __init__.py
    ├── module1.py
    └── module2.py

У цьому прикладі my_package – це пакет, а module1.py та module2.py – модулі всередині нього.

Механізм пошуку модулів: sys.path

Коли Python виконує інструкцію:

import my_module

інтерпретатор шукає файл my_module.py або пакет з такою назвою у директоріях, визначених у sys.path. Пошук відбувається у строгій послідовності:

  1. Директорія скрипту, який запускається. Інакше кажучи каталог, в якому знаходиться .py файл, що був запущений.
  2. Директорії зі змінної середовища PYTHONPATH – якщо ця змінна налаштована.
  3. Стандартні системні директорії Python, включно з каталогами встановлених бібліотек (наприклад, site-packages).

Подивитись поточний шлях пошуку можна так:

import sys

print(sys.path)

Це допомагає діагностувати помилки імпорту, особливо коли модуль знаходиться не там, де очікує інтерпретатор.

Детальніше про модуль sys у Python.

Синтаксис інструкції import

import <module>

Імпортує модуль як єдиний об’єкт:

# my_package/module1.py
def func():
    return 'Hello from module1'

Доступ до атрибутів модуля (змінних, функцій, класів) відбувається через крапку:

# main.py
import my_package.module1

print(my_package.module1.func())

from <package> import <module_or_attribute>

Імпортує конкретний атрибут (модуль, функцію, клас) безпосередньо у поточний простір імен:

# main.py
from my_package.module1 import func

print(func())  # Прямий доступ без префіксу

import ... as <alias>

Створює псевдонім (alias) для імпортованого модуля або атрибута. Це корисно для уникнення конфліктів імен або скорочення довгих назв:

# main.py
import my_package.module1 as m1
from my_package.module1 import func as f1

print(m1.func())
print(f1())

from <module> import *

Імпортує всі публічні імена з модуля (ті, що не починаються з _). Не рекомендується для використання у продакшн-коді – це забруднює простір імен і ускладнює читання.

Абсолютний та відносний імпорт

При роботі всередині пакета Python підтримує два види імпорту: абсолютний та відносний.

Абсолютний імпорт

Абсолютний імпорт вказує повний шлях до модуля, починаючи від однієї з директорій, що перелічені у sys.path:

# my_package/module2.py
from my_package import module1  # Абсолютний імпорт


def call_module1():
    return module1.func()

Відносний імпорт

Вказує шлях до модуля відносно поточного файлу. Для цього використовуються крапки:

  • . – поточний пакет
  • .. – пакет на рівень вище

Структура проєкту:

my_project/

├── app/
│   ├── __init__.py
│   │
│   ├── another_package/
│   │   ├── __init__.py
│   │   └── module3.py
│   │
│   └── my_package/
│       ├── __init__.py
│       ├── module1.py
│       └── module2.py

└── main.py

Файл app/my_package/module2.py:

# Відносні імпорти у Python

# Імпорт module1 з тієї ж директорії (my_package)
from . import module1

# Імпорт module3 із сусіднього пакета another_package
from ..another_package import module3


def call_module1_relative():
    return module1.func()


def call_module3_relative():
    return module3.func()

Примітка: Відносні імпорти працюють лише всередині пакетів. Якщо запустити модуль з відносним імпортом як окремий скрипт верхнього рівня, Python згенерує ImportError.

Поширена проблема: Циклічний імпорт (Circular import)

Циклічний імпорт виникає, коли moduleA імпортує moduleB, а moduleB у свою чергу імпортує moduleA. Це призводить до ImportError або AttributeError, оскільки один з модулів не буде повністю ініціалізований на момент звернення до нього.

Як уникнути циклічного імпорту:

  • Рефакторинг структури: Винести спільну логіку у третій модуль, який можуть імпортувати обидва.
  • Локальний імпорт: Імпортувати модуль всередині функції або методу, де він потрібен. Це відкладає імпорт до моменту виклику функції.

Приклад локального імпорту:

# moduleA.py
def my_func():
    from . import moduleB # Імпорт відбувається лише при виклику my_func()
    # ...

Висновки та кращі практики

  • Надавайте перевагу абсолютному імпорту – він більш явний, надійний та полегшує навігацію в проєкті.
  • Уникайте from <module> import * – такий імпорт забруднює простір імен і ускладнює читабельність.
  • Застосовуйте псевдоніми (as), коли потрібно скоротити довгі назви або уникнути конфліктів між іменами.
  • Організовуйте код у пакети, щоб логічно розділяти функціональність та полегшувати імпорт.
  • Стежте за залежностями між модулями, щоб уникати циклічних імпортів і пов’язаних з ними помилок.

Мітки: