Как создать, импортировать и повторно использовать собственный модуль в Python

Что такое модульность кода и зачем её использовать
Модульность — это разбиение программы на независимые части с чёткими обязанностями. Коротко: разделяйте ответственность, чтобы писать и тестировать код быстрее. Модульность помогает соблюдать правило “Не повторяйся” (DRY). Одна функция или класс реализует логику — вы вызываете её в нескольких местах вместо копирования кода.
Преимущества:
- Улучшает читаемость и поддержку.
- Упрощает тестирование компонентов.
- Позволяет повторно использовать код в разных проектах.
- Делает возможным изоляцию багов и профилирование узких мест.
Важно: модуль может быть простым набором функций или классом в файле .py. В статье мы покажем оба подхода на примере счётчика слов.
Объектно-ориентированное программирование в Python
OOP представляет код в виде классов и экземпляров. Класс описывает свойства (атрибуты) и поведение (методы). Экземпляры класса хранят состояние и вызывают методы.
Кратко по терминам:
- Класс — шаблон для объектов.
- Экземпляр — объект, созданный из класса.
- Атрибут — данные, принадлежащие классу или экземпляру.
- Метод — функция, связанная с классом.
OOP полезно, когда вам нужно объединять данные и операции над ними или наследовать и расширять поведение.
Практический пример: модуль счётчика слов
Ниже показан рабочий пример, как превратить небольшой скрипт в переиспользуемый модуль и как импортировать его из разных мест проекта.
Шаги:
- Создайте каталог проекта (пример названия — word_count):
mkdir word_count- Создайте виртуальное окружение и активируйте его (по желанию). Убедитесь, что находитесь в каталоге проекта.
- Создайте файл counter.py в каталоге, содержащий класс WordCounter.
Пример простого рабочего скрипта (для идеи):
sentence = 'how to make a reusable word counter in python'
words = sentence.split(' ')
count = sum(1 for word in words if word)
print(count) # 9Преобразим это в класс в файле counter.py:
class WordCounter:
"""Класс для подсчёта слов в строке."""
def count_words(self, sentence: str) -> int:
"""Возвращает количество непустых слов в предложении.
Один строковый аргумент, разделитель — пробел. Это минимальная реализация.
"""
if sentence is None:
return 0
words = sentence.split(' ')
count = sum(1 for word in words if word)
return countТеперь у вас есть переиспользуемый модуль — файл counter.py с классом WordCounter.
Импорт модуля из той же папки
Если другой скрипт лежит в той же папке, импорт прост:
Создайте файл main.py в той же папке и напишите:
from counter import WordCounter
sentence = "how to import and reuse your code in Python"
counter = WordCounter()
print(counter.count_words(sentence)) # 7Пояснения:
- Python ищет модуль в текущем каталоге, затем в sys.path.
- Имена файлов и классов чувствительны к регистру на большинстве ОС.
Импорт модуля из другой папки
Если файл, который импортирует модуль, находится в другой папке проекта, есть несколько подходов. Мы рассмотрим три безопасных варианта: пакеты с init.py, относительный импорт внутри пакета и установка разработки через pip (-e).
1) Пакет с init.py и абсолютный импорт
Структура проекта:
word_count
├─ destination
│ └─ destination.py
└─ wordcount
├─ __init__.py
└─ counter.pyФайл wordcount/init.py можно оставить пустым или экспортировать класс:
from .counter import WordCounter
__all__ = ["WordCounter"]В destination.py используйте абсолютный импорт:
from wordcount.counter import WordCounter
sentence = 'import and reuse your Python code from files with different paths'
counter = WordCounter()
print(counter.count_words(sentence))Важно: чтобы Python видел пакет wordcount при запуске destination.py напрямую, точка запуска должна быть корнем проекта (т.е. запустить python -m destination.destination или настроить PYTHONPATH). Лучше запускать скрипт из корня проекта:
cd word_count
python destination/destination.py2) Относительный импорт внутри пакета
Если destination — также пакет, можно применять относительный импорт. Структура:
word_count
└─ app
├─ __init__.py
├─ destination.py
└─ wordcount
├─ __init__.py
└─ counter.pyВ destination.py:
from .wordcount.counter import WordCounterЗапускать модуль следует как пакет:
python -m app.destination3) Установка в editable режиме (pip install -e)
Для реальных проектов и многократного использования удобнее оформить пакет и установить его в виртуальном окружении:
- Создайте pyproject.toml или setup.cfg и setup.py (в простом виде). Затем выполните:
pip install -e .После этого вы сможете импортировать модуль из любой точки, так как пакет будет доступен как установленный.
Временный трюк с sys.path
Иногда встречается код, расширяющий sys.path вручную:
import sys
sys.path.append(sys.path[0] + "/..")
from wordcount.counter import WordCounterЭто быстро решает проблему при запуске из IDE или скрипта, но не является хорошей практикой для долгосрочного проекта. Используйте его только для локального теста.
Дополнительные рекомендации и улучшения модуля
- Добавьте обработку пунктуации и множественных пробелов, если нужна точная статистика.
- Предусмотрите опцию нормализации регистра (lowercase) при подсчёте уникальных слов.
- Напишите юнит-тесты для класса WordCounter.
- Документируйте поведение в docstring и README.
Пример расширенного метода:
import re
class WordCounter:
def count_words(self, sentence: str) -> int:
if not sentence:
return 0
words = re.findall(r"\b\w+\b", sentence)
return len(words)Это регулярное выражение считает слова как последовательности буквенно-цифровых символов, игнорируя пунктуацию.
Тесты и критерии приёмки
Критерии приёмки для базового модуля:
- Функция должна возвращать 0 для пустой строки или None.
- Для строки “hello world” должно вернуть 2.
- Для строки с пунктуацией и пробелами результат должен быть корректным.
- Методы должны иметь предсказуемые типы входа и выхода (str -> int).
Пример простого pytest теста (tests/test_counter.py):
from wordcount.counter import WordCounter
def test_empty():
assert WordCounter().count_words('') == 0
def test_simple():
assert WordCounter().count_words('hello world') == 2
def test_punctuation():
assert WordCounter().count_words('hello, world!') == 2Когда модульность не помогает — контрпримеры и ограничения
- Для однострочных скриптов, которые никогда не будут поддерживаться, создание модуля может быть излишним.
- Переусложнение интерфейса (слишком много параметров) делает повторное использование труднее.
- Если у вас есть высоконагруженные узлы, простая рефакторинг-модуль может не решить проблем производительности — нужно профилировать.
Безопасность и приватность
- Не храните в модуле секреты (пароли, ключи). Используйте переменные окружения или менеджеры секретов.
- При импорте динамических модулей избегайте выполнения непроверённого кода через importlib или exec.
Шаблон плейбука для создания и распространения модуля
- Выделите ответственность — одна задача на модуль.
- Напишите чистую реализацию и docstring.
- Добавьте юнит-тесты.
- Создайте пакет (pyproject.toml) и README.
- Установите в editable режим для разработки: pip install -e .
- Документируйте API и примеры использования.
Чех-лист ролей
Для разработчика:
- Есть docstring и README.
- Написаны тесты.
- Обработаны граничные случаи.
Для тимлида:
- Интерфейс прост и понятен.
- Нет глобальных побочных эффектов.
Для тестировщика:
- Покрытие тестами критичных сценариев.
- Проверки на некорректные входные данные.
Быстрая памятка по импортам (cheat sheet)
- Локальный импорт в той же папке: from module import Class
- Абсолютный импорт из пакета: from package.module import Class
- Относительный импорт внутри пакета: from .module import Class
- Установка пакета: pip install -e .
Пример структуры для реального проекта
word_count/ # корень проекта
├─ pyproject.toml
├─ README.md
├─ wordcount/ # пакет с кодом
│ ├─ __init__.py
│ └─ counter.py
└─ tests/
└─ test_counter.pyМентальные модели и эвристики
- Делайте модули маленькими и автономными.
- Если вы тянете один и тот же файл в три проекта — сделайте из него пакет.
- Тесты — главный индикатор пригодности к повторному использованию.
Краткий итог
Повторное использование кода в Python делается легко: оформите файл как модуль или пакет, выберите подходящий тип импорта и тестируйте. Для небольших проектов хватает простого импорта по пути, для устойчивых решений — оформляйте пакет и используйте editable-установку или публикуйте пакет.
Important: избегайте изменения sys.path в продакшн-коде и не храните секреты в модуле.
Похожие материалы
Как включить iCloud Photos и синхронизировать фото
Aitum Vertical Plugin для OBS — вертикальный стриминг
Как отформатировать SD‑карту на Mac
Перенос чатов WhatsApp с iPhone на Android
Как снимать ночью: избавляемся от смаза