Модули и пакеты
Теория
Когда кода становится много, его раскладывают по файлам: расчёт цен — в 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. Подключаем модули стандартной библиотеки
Три формы импорта на знакомых модулях:
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:
TAX_RATE = 20 def apply_discount(price, percent): return price * (100 - percent) // 100 def with_tax(price): return price + price * TAX_RATE // 100
Импортируем его в основной программе и обращаемся к именам через точку:
import pricing print(pricing.apply_discount(1000, 10), pricing.with_tax(1000), pricing.TAX_RATE)
Результат:
900 1200 20
Шаг 3. Импортируем отдельные имена
from ... import кладёт имя прямо в программу; as переименовывает:
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:
print("загружаю catalog.py") PRODUCTS = {42: "Мышь", 7: "Клавиатура"}
Импортируем дважды:
import catalog import catalog print(catalog.PRODUCTS[42])
Результат:
загружаю catalog.py Мышь
Второй import взял модуль из кэша и файл не выполнял.
Шаг 5. Разделяем запуск и импорт через name
Скрипт рассылки: функция плюс «ручной запуск». Файл notify.py:
def send_promo(emails): for email in emails: print(f"письмо отправлено: {email}") print("в notify.py __name__ =", __name__) if __name__ == "__main__": send_promo(["anna@shop.test"])
Импортируем его и используем только функцию:
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:
# пакет shop — модули магазинаФайл shop/cart.py:
def calc_total(prices): return sum(prices)
Импортируем функцию из модуля пакета — путь записывается через точку:
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 под капотом», тема «Импорт изнутри».