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

Теория

Когда кода становится много, его раскладывают по файлам: расчёт цен — в pricing.py, работа с корзиной — в cart.py, отправка писем — в notify.py. Каждый .py-файл — это модуль, и функции из него подключают инструкцией import. Папка с модулями и файлом __init__.py — пакет: так устроены и большие библиотеки, и Django-проект (shop/models.py, shop/views.py). Стандартная библиотека Python — тоже набор модулей: json, datetime, decimal, re.

При первом import Python выполняет весь файл модуля сверху вниз и запоминает получившийся объект модуля; повторные импорты берут его из кэша и файл заново не выполняют. Отсюда важное правило: на верхнем уровне модуля должны быть только определения — функции, классы, константы. Код, который что-то делает (отправляет письма, пишет в базу), прячут под if __name__ == "__main__": — этот блок выполняется, только когда файл запущен как программа (python notify.py), и не выполняется при импорте.

Наглядно

Таблица 1. Формы импорта.

Запись Что появляется в программе Как обращаться
import pricing имя модуля pricing.apply_discount(...)
from pricing import apply_discount имя функции apply_discount(...)
import pricing as pr модуль под другим именем pr.apply_discount(...)
from pricing import apply_discount as discount функция под другим именем discount(...)
from shop.cart import calc_total функция из модуля пакета calc_total(...)
from pricing import * все публичные имена модуля не используйте — непонятно, откуда имя

Таблица 2. Значение __name__.

Как используется файл __name__ Блок if __name__ == "__main__":
запущен как программа: python notify.py "__main__" выполняется
импортирован: import notify "notify" (имя модуля) не выполняется

На практике

Задача: вынести расчёт цен в отдельный модуль, собрать небольшой пакет shop и сделать скрипт рассылки, который безопасно импортировать.

Шаг 1. Подключаем модули стандартной библиотеки

Три формы импорта на знакомых модулях:

python
import json
from decimal import Decimal
import datetime as dt

print(json.dumps({"price": str(Decimal("1499.90"))}))
print(dt.date(2026, 10, 20))

Результат:

вывод
{"price": "1499.90"}
2026-10-20

Шаг 2. Создаём свой модуль

Модуль — обычный файл с функциями и константами. Файл pricing.py:

python
TAX_RATE = 20


def apply_discount(price, percent):
    return price * (100 - percent) // 100


def with_tax(price):
    return price + price * TAX_RATE // 100

Импортируем его в основной программе и обращаемся к именам через точку:

python
import pricing

print(pricing.apply_discount(1000, 10), pricing.with_tax(1000), pricing.TAX_RATE)

Результат:

вывод
900 1200 20

Шаг 3. Импортируем отдельные имена

from ... import кладёт имя прямо в программу; as переименовывает:

python
from pricing import apply_discount
from pricing import with_tax as add_tax

print(apply_discount(5990, 15), add_tax(490))

Результат:

вывод
5091 588

Шаг 4. Видим, что модуль выполняется один раз

Добавим в модуль вывод на верхнем уровне. Файл catalog.py:

python
print("загружаю catalog.py")

PRODUCTS = {42: "Мышь", 7: "Клавиатура"}

Импортируем дважды:

python
import catalog
import catalog

print(catalog.PRODUCTS[42])

Результат:

вывод
загружаю catalog.py
Мышь

Второй import взял модуль из кэша и файл не выполнял.

Шаг 5. Разделяем запуск и импорт через name

Скрипт рассылки: функция плюс «ручной запуск». Файл notify.py:

python
def send_promo(emails):
    for email in emails:
        print(f"письмо отправлено: {email}")


print("в notify.py __name__ =", __name__)

if __name__ == "__main__":
    send_promo(["anna@shop.test"])

Импортируем его и используем только функцию:

python
import notify

notify.send_promo(["oleg@shop.test"])
print("в основной программе __name__ =", __name__)

Результат:

вывод
в notify.py __name__ = notify
письмо отправлено: oleg@shop.test
в основной программе __name__ = __main__

Письмо Анне при импорте не ушло: у импортированного модуля __name__ — его имя, а не "__main__". При запуске python notify.py блок выполнился бы.

Шаг 6. Собираем пакет

Пакет — папка с модулями и файлом __init__.py. Файл shop/__init__.py:

python
# пакет shop — модули магазина

Файл shop/cart.py:

python
def calc_total(prices):
    return sum(prices)

Импортируем функцию из модуля пакета — путь записывается через точку:

python
from shop.cart import calc_total

print(calc_total([1499, 490]))

Результат:

вывод
1989

Так же устроен Django-проект: from shop.models import Product.

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

  • Действия на верхнем уровне модуля. Отправка писем, запись в базу, долгие расчёты выполнятся при каждом импорте. Такой код — в функции или под if __name__ == "__main__":.
  • Одинаковые имена из разных модулей. from pricing import cost и from shipping import cost — второе перезапишет первое. Импортируйте модули (pricing.cost, shipping.cost) или переименуйте через as.
  • from module import *. Непонятно, откуда взялось имя, и легко перезаписать свои. Импортируйте явно.
  • Свой файл с именем модуля стандартной библиотеки. Файл json.py или random.py в папке проекта «закроет» настоящий модуль: import json найдёт ваш файл, и посыплются странные ошибки вроде module 'json' has no attribute 'dumps'.
  • Циклический импорт. cart.py импортирует pricing.py, а тот — cart.py: один из модулей получит недогруженного соседа. Общий код выносят в третий модуль.

Итог

Модули

  • Модуль — .py-файл; import выполняет его один раз и кэширует.
  • Формы: import m, from m import name, as для переименования; import * — не используем.

Пакеты

  • Папка с __init__.py и модулями; импорт через точку: from shop.cart import calc_total.

__name__

  • Запущен как программа — "__main__", импортирован — имя модуля.
  • На верхнем уровне модуля — только определения; действия — под if __name__ == "__main__":.

Вопросы на собеседовании

Зачем нужен if __name__ == "__main__":?

Чтобы код выполнялся только при запуске файла как программы, а не при его импорте. У импортированного модуля __name__ равен его имени, у запущенного — "__main__". Так один файл служит и библиотекой функций, и скриптом.

Что происходит при import module?

Python ищет модуль по путям из sys.path, выполняет его код сверху вниз, создаёт объект модуля и кэширует его в sys.modules. Повторный импорт берёт объект из кэша и код заново не выполняет.

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

Модуль — один файл .py. Пакет — папка, в которой лежат модули (и вложенные пакеты) и файл __init__.py; к модулям пакета обращаются через точку: shop.cart.

Глубже

Как Python ищет модули, что лежит в sys.modules и как разорвать циклический импорт — в курсе «Python под капотом», тема «Импорт изнутри».

Ошибка в тексте

Сообщить об ошибке могут зарегистрированные пользователи — так в сообщениях меньше спама.

Войти, чтобы сообщить