Пакеты в Python

Пакеты в Python: как организовать код в директории с __init__.py, импортировать модули и использовать пространства имён.

Пакет — это директория, содержащая файл __init__.py и один или несколько модулей (файлов .py). Пакеты позволяют группировать связанный код и избегать конфликтов имён.

Зачем нужны пакеты

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

Структура пакета

Предположим, вы разрабатываете игру. Вот типичная структура пакетов:

# Структура директорий:
# game/
#     __init__.py          ← делает папку пакетом
#     settings.py
#     level/
#         __init__.py
#         start.py
#         finish.py
#     player/
#         __init__.py
#         hero.py
#         enemy.py

Файл __init__.py может быть пустым — само его присутствие сообщает Python: «эта папка — пакет». При желании в него помещают инициализационный код: импорты, константы, версию пакета.

Три способа импортировать из пакета

Способ 1: импорт целого модуля через точку

import game.level.start

# Обращаемся через полный путь
game.level.start.select_difficulty(2)

Явно, но длинно. Подходит, когда важно видеть, откуда пришла функция.

Способ 2: импорт модуля с псевдонимом

from game.level import start

# Обращаемся только через имя модуля
start.select_difficulty(2)

Короче, пространство имён сохраняется.

Способ 3: прямой импорт объекта

from game.level.start import select_difficulty

# Вызываем напрямую
select_difficulty(2)

Самый лаконичный, но опасный при конфликтах имён. Если в другом модуле тоже есть select_difficulty, последний импорт перезапишет предыдущий.

Правило: чем длиннее путь при импорте, тем безопаснее — конфликт имён исключён. Используйте способ 3 только для часто вызываемых функций из хорошо известных модулей.

Файл __init__.py: что в него кладут

# game/__init__.py

VERSION = "1.0.0"

# Реэкспорт часто используемых объектов, чтобы пользователи
# пакета могли писать: from game import Hero
from game.player.hero import Hero

После этого пользователь пишет коротко:

from game import Hero  # работает, хотя Hero определён глубоко внутри

print(Hero)

Вывод (при наличии класса Hero):

<class 'game.player.hero.Hero'>

Относительные импорты

Внутри пакета модули могут импортировать друг друга относительно текущего местоположения:

# game/level/finish.py

from . import start           # . = текущий пакет (game/level)
from ..player import hero     # .. = родительский пакет (game), затем player

Точка означает «текущий пакет», две точки — «родительский». Относительные импорты работают только внутри пакета, в скриптах верхнего уровня их использовать нельзя.

Как Python ищет пакеты: sys.path

import sys

# Python ищет пакеты и модули в этих директориях по порядку
for path in sys.path:
    print(path)

Вывод (пример):

/Users/user/project
/usr/lib/python3.12
/usr/lib/python3.12/lib-dynload
/usr/local/lib/python3.12/site-packages

Первой стоит директория текущего скрипта. Дальше — стандартная библиотека и site-packages (туда pip устанавливает сторонние пакеты).

Сторонние пакеты

Установка через pip и импорт работают так же, как для собственных пакетов:

# В терминале: pip install requests

import requests  # теперь пакет доступен

response = requests.get("https://httpbin.org/get")
print(response.status_code)

Вывод:

200

Частые ошибки

ОшибкаПричинаРешение
ModuleNotFoundErrorдиректория не в sys.path или нет __init__.pyпроверьте структуру, добавьте __init__.py
конфликт имён при from x import *перезапись глобальных имёниспользуйте явные импорты
циклический импортмодуль A импортирует B, B импортирует Aвынесите общий код в третий модуль

Коротко

  • Пакет — директория с файлом __init__.py; он может быть пустым.
  • Три способа импорта: через точку, from pkg import module, from pkg.module import obj.
  • Чем полнее путь импорта, тем меньше риск конфликта имён.
  • Python ищет пакеты по sys.path; pip устанавливает в site-packages.
  • Относительные импорты (from . import x) — только внутри пакета.
Проверьте себя
1. Какой файл обязательно должен находиться в папке, чтобы Python воспринимал её как пакет?
Amain.py
Bsetup.py
C__init__.py
D__package__.py
2. Как вызвать функцию select_difficulty() из модуля Game.Level.start после команды import Game.Level.start?
Aselect_difficulty(2)
Bstart.select_difficulty(2)
CGame.Level.start.select_difficulty(2)
DLevel.start.select_difficulty(2)
3. Почему не рекомендуется использовать from Game.Level.start import select_difficulty?
AИмя select_difficulty может конфликтовать с другими идентификаторами с тем же именем в проекте
BЭто замедляет выполнение программы
CPython не поддерживает такой синтаксис
DФункция из пакета не может быть импортирована напрямую
4. Как Python ищет пакеты при их импорте?
AТолько в текущей директории
BТолько в стандартной библиотеке
CИсключительно через переменную окружения PYTHONPATH
DПо тем же правилам, что и модули: через sys.path
py
Курс по теме
Пройдите курс «Python с нуля» — по шагам, с проверкой
8 уроков · ~14 ч · теория, упражнения и экзамен с бейджем
Открыть курс →