Модули в Python

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

Что такое модуль?

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

Модули помогают:

  • Организовать связанный код в отдельные файлы
  • Повторно использовать код в разных программах
  • Избежать конфликтов имен
  • Сделать код более читаемым и поддерживаемым

Создание собственного модуля и его импорт

Создать модуль в Python очень просто — достаточно написать код в файле с расширением .py. Заведём mymath.py и тут же положим рядом несколько main-скриптов, которые показывают разные способы его подключить:

main_basic.py
1# Импорт всего модуля2import mymath3 4# Все имена доступны через префикс mymath.5 6result = mymath.add(5, 3)7print(f"5 + 3 = {result}") # 5 + 3 = 88 9area = mymath.PI * 5 ** 210print(f"Площадь круга радиусом 5: {area}")11 12# Площадь круга радиусом 5: 78.5397513 14 

В дереве слева — наш модуль mymath.py и четыре варианта main_*.py, каждый показывает свой способ импорта. Переключайтесь между вкладками. Ниже короткий разбор, когда какой удобнее.

Импорт всего модуля

Самый прямой способ — import mymath (см. main_basic.py). Все имена остаются «внутри» модуля и доступны через префикс: mymath.add, mymath.PI. Удобно, когда из одного модуля нужно много функций и важно видеть, откуда они пришли.

Импорт конкретных элементов

from mymath import add, multiply (см. main_specific.py). Импортируем только нужное и обращаемся без префикса. Хорошо для пары часто вызываемых функций; плохо, когда через сотню строк забываешь, откуда взялась add.

Импорт с переименованием

import mymath as mm (см. main_aliased.py). Нужно, если имя модуля длинное (numpy as np, pandas as pd — те самые случаи) или конфликтует с локальной переменной.

Импорт всех элементов

from mymath import * (см. main_star.py). Импортирует всё подряд в текущее пространство имён. В обычном коде так делать обычно не стоит: теряется источник имён, легко получить конфликт. Нормально только в REPL и иногда в тестах.

Структура модуля

Хороший модуль обычно содержит блоки в таком порядке:

  1. Документация — строка в тройных кавычках в самом начале файла, описывает назначение модуля.
  2. Импорты — сначала стандартная библиотека, затем сторонние пакеты, затем собственные модули.
  3. Константы — глобальные значения, которые не меняются в работе.
  4. Классы — определения классов.
  5. Функции — определения функций.
  6. Код для выполнения — блок if __name__ == "__main__": для запуска модуля как самостоятельной программы (про него ниже).

Если посмотреть на наш mymath.py, то документация уже есть в первой строке, константа PI на месте, дальше идут функции. Импортов нет (модуль самодостаточный), классов нет (для арифметики они не нужны). Блок «код для выполнения» добавим через секцию, в разговоре про __name__.

Пакеты

Когда модуль mymath.py разрастается (например, появляются разные группы операций), его можно разделить на несколько файлов и собрать в пакет. Пакет в Python — это директория с файлом __init__.py. Когда вы пишете import mathlib, Python видит этот __init__.py и понимает, что директория — это импортируемый пакет.

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

mathlib/__init__.py
1"""mathlib: пакет с математическими функциями."""2 3**version** = "0.1"4 5# Re-export, чтобы пользователи могли писать from mathlib import add6 7from mathlib.basic import PI, add, subtract8from mathlib.advanced import multiply, divide9 

Что изменилось:

  • Файлов теперь четыре, но снаружи это всё ещё «один математический модуль» — пользователь пишет from mathlib import add, как и раньше с mymath.
  • mathlib/__init__.py контролирует, что считается публичным API: имена, перечисленные через from .basic import ... и from .advanced import ..., доступны прямо как mathlib.add.
  • Внутри пакета модули могут ссылаться друг на друга через относительные импорты: from . import basic (тот же каталог), from .. import other (родительский каталог).

Если пакет растёт ещё, его можно разбить на подпакеты — например, mathlib/stats/ со своим __init__.py и модулями статистических функций. Принцип тот же, просто на уровень глубже.

Специальные переменные модуля

В Python модули имеют несколько специальных переменных:

Переменная __name__: модуль как программа vs как зависимость

Внутри Python у каждого модуля есть переменная __name__. Когда модуль импортируют, в ней лежит его имя ("mymath"). А когда модуль запускают напрямую (python mymath.py), Python кладёт в неё специальное значение "__main__". Это позволяет внутри модуля написать блок «делай это только при прямом запуске»:

mymath.py
1"""Модуль с математическими функциями."""2 3PI = 3.141594 5def add(a, b):6return a + b7 8def divide(a, b):9if b == 0:10raise ValueError("Деление на ноль невозможно")11return a / b12 13# Этот блок выполняется только при прямом запуске:14 15# python mymath.py16 17# При импорте (import mymath) в другом файле он не сработает.18 19if **name** == "**main**":20print(f"PI = {PI}")21print(f"add(2, 3) = {add(2, 3)}")22 

Запустите python mymath.py — увидите вывод if-блока. Запустите python main.py — этот блок промолчит, и в выводе будет только 8 от вызова в main.py.

Переменная __all__: что попадает в from module import *

По умолчанию from mymath import * импортирует все имена, которые не начинаются с подчёркивания. Если хочется явно зафиксировать публичный API модуля, добавляют переменную __all__ со списком имён.

mymath.py
1"""Модуль с математическими функциями."""2 3# Только эти имена попадут в from mymath import *4 5**all** = ["PI", "add"]6 7PI = 3.141598_INTERNAL = "не должен попасть наружу"9 10def add(a, b):11return a + b12 13def subtract(a, b):14return a - b15 16def _round_helper(value):17"""Внутренний помощник, не для публичного использования."""18return round(value, 2)19 

Через from mymath import * пришли только PI и add — именно те, что перечислены в __all__. Остальные имена (subtract, _INTERNAL, _round_helper) остались в модуле и доступны только при явном импорте: from mymath import subtract.

Лучшие практики при работе с модулями

  1. Один модуль — одна ответственность: Каждый модуль должен отвечать за одну конкретную функциональность.

  2. Понятные имена модулей: Используйте описательные, но краткие имена (например, data_processing.py, user_interface.py).

  3. Документация: Каждый модуль должен начинаться с документации, описывающей его назначение.

  4. Импорты в начале файла: Все импорты должны быть в начале модуля.

  5. Явные импорты: Предпочитайте from module import specific_thing вместо from module import *.

  6. Приватные имена: Начинайте имена "приватных" функций и переменных с подчеркивания (_private_function).

  7. Тестирование модуля: Используйте блок if __name__ == "__main__": для тестирования модуля.

Проверка понимания

Давайте проверим, насколько хорошо вы усвоили тему модулей:

Что будет выведено при запуске main.py из проекта ниже?

main.py
1import test_module2 3print(test_module.hello())4 

Помните, модули и пакеты помогают применять принцип "Не повторяйся" (DRY - Don't Repeat Yourself) и делают ваши программы более профессиональными. Организуйте свой код мудро, и вам будет намного легче развивать его в будущем! 💪