Чем отличается пакет от модуля в Python?

Ответ

Модуль — это одиночный файл с расширением .py, содержащий исполняемый код Python (функции, классы, переменные). Пакет — это директория, которая содержит один или несколько модулей (или вложенных пакетов) и специальный файл __init__.py.

Простая аналогия: модуль — это файл, пакет — это папка с файлами.

Пример структуры:

my_project/
├── main.py
├── utils.py               # <-- Это модуль
└── my_package/            # <-- Это пакет
    ├── __init__.py        # <-- Инициализирует пакет (может быть пустым)
    ├── core.py            # <-- Модуль внутри пакета
    └── helpers/           # <-- Вложенный пакет
        ├── __init__.py
        └── validator.py

Ключевые отличия:

  1. Организация: Пакет позволяет структурировать код в иерархии, модуль — это плоская единица.
  2. Файл __init__.py: Его наличие (даже пустого) делает директорию пакетом, который Python может импортировать. В Python 3.3+ для пространства имён (namespace package) он может не требоваться, но для обычных пакетов — это стандарт.
  3. Импорт:

    # Импорт модуля
    import utils
    from utils import some_function
    
    # Импорт из пакета
    import my_package.core
    from my_package.helpers import validator
  4. Относительный импорт: Внутри пакета можно использовать относительные импорты (например, from .core import something), что невозможно в одиночном модуле.

Ответ 18+ 🔞

Давай разберёмся, что тут к чему, а то у некоторых от этих терминов глаза на лоб лезут, как будто им про квантовую физику рассказывают.

Представь себе такую хуйню. Модуль — это просто один файлик с расширением .py. В нём твой код живёт: функции, классы, переменные. Один файл — один модуль. Всё просто, как палка.

А вот пакет — это уже не файл, а целая папка. Но не простая папка, а хитрая жопа. Внутри неё должны лежать другие модули (или даже другие вложенные папочки-пакеты) и обязательно специальный файл __init__.py. Без этого файла — это просто директория, а не пакет. С ним — уже полноценная единица, которую Питон может импортировать.

Проще говоря: модуль — это файл, пакет — это папка с файлами. Ёпта, вроде бы и ебушки-воробушки, но народ постоянно путается.

Вот смотри, как это выглядит на практике:

my_project/
├── main.py
├── utils.py               # <-- Вот это модуль, один файл
└── my_package/            # <-- А вот это уже пакет, папка
    ├── __init__.py        # <-- Волшебный файл, инициализирует пакет. Может быть и пустым, главное — чтобы был.
    ├── core.py            # <-- Модуль, но уже внутри пакета
    └── helpers/           # <-- Вложенный пакет, ёклмн!
        ├── __init__.py
        └── validator.py

А теперь главные отличия, чтобы ты не облажался:

  1. Организация. Пакет — это иерархия, структура. Можно делать вложенности, как в хорошем шкафу. Модуль — это просто один ящик. Всё скинул в кучу — и похуй.

  2. Файл __init__.py. Это маркер. Его наличие (даже пустого) кричит Питону: «Эй, я не просто папка, я — пакет, импортируй меня!». Хотя в современных версиях (3.3+) для каких-то особых «пространств имён» он может и не нужен, но для обычных смертных проектов — делай его всегда. Не умничай.

  3. Импорт. Тут всё логично, но у людей терпения ноль ебать, и они ломаются.

    # Импортируем просто модуль (файл)
    import utils
    from utils import some_function
    
    # Импортируем из пакета (папки)
    import my_package.core
    from my_package.helpers import validator
  4. Относительный импорт. Вот это мощная фишка пакетов, которая в одиночных модулях не работает. Когда ты внутри пакета, можешь импортировать соседние модули относительно текущего. Например, из helpers/validator.py можно написать from ..core import something (две точки — подняться на уровень выше). В одиночном файле utils.py так не получится — он нихуя не знает про свою «семью».

Короче, если проект маленький — хватит и модулей. Но если код растёт как на дрожжах, и в голове уже каша — дроби всё на пакеты. И да пребудет с тобой порядок, а не пиздец.