Модуль 3 · Урок 12 із 58

Модулі, imports і __main__: структура малої програми

Модуль — не просто другий файл. Він створює окремий namespace і boundary відповідальності. Вдалий поділ дозволяє імпортувати core без запуску CLI, тестувати функції напряму й бачити dependencies за import-ами, а не копіювати definitions між скриптами.

Modulesimport__main__Package layout

Безпечна практика модуля 3

Пакет містить тільки синтетичні function contracts, import-safe modules і локальні tests. Жодних реальних звернень, персональних даних, secrets, мережевих викликів або зовнішніх залежностей.

Завантажити практичний пакет →

Один .py файл — один module namespace

Після import src.routing Python виконує module code один раз у поточній interpreter session, створює module object і bind-ить ім’я. Definitions не потрапляють автоматично в caller namespace.

import src.routing

label = src.routing.route_case(75, has_blocker=False)

Qualified name показує origin function і зменшує collisions. from src.routing import route_case теж валідний, якщо local context лишається однозначним. Wildcard import приховує source names і не підходить як звичайна структура застосунку.

Import не повинен випадково запускати CLI

# src/main.py
from src.routing import route_case

def main() -> None:
    label = route_case(75, has_blocker=False)
    print(f"route={label}")

if __name__ == "__main__":
    main()

При прямому запуску module має __name__ == "__main__", тому викликає CLI. Під час import його __name__ є module name, і guard не запускає display side effect. Definitions лишаються доступними для тестів.

Структура за напрямком залежностей

python-module3-functions/
├── src/
│   ├── __init__.py
│   ├── routing.py      # pure core
│   ├── reporting.py    # format result
│   └── main.py         # CLI shell
└── tests/
    └── test_routing.py

main залежить від core, але core не імпортує main. Такий напрямок уникає circular import. Якщо два модулі постійно імпортують один одного, responsibilities або shared types, імовірно, розміщено невдало.

Module search path — частина середовища

Python шукає built-ins, а потім modules у sys.path. Current working directory, спосіб запуску, installed package і PYTHONPATH можуть змінити результат. Не виправляйте import додаванням випадкових абсолютних шляхів у code. Запускайте команду з documented project root, використовуйте package layout і перевіряйте sys.executable та module.__file__ під час діагностики.

Не називайте навчальний файл як stdlib module

json.py, csv.py або unittest.py у project root можуть shadow офіційний module. Симптом — import завантажує ваш файл або виникає дивний circular import.

Package boundary і __init__.py

Для простого навчального package порожній __init__.py робить boundary явною та стабільною для інструментів. Не переносіть у нього всі imports «для зручності»: це може створити eager side effects і cycles. Public API package проєктують свідомо, коли вже є реальна потреба.

Практика: imports без мережі й secrets

Пакет модуля містить pure routing functions, reporting module, окремий CLI entry і п’ять unittest cases. Запустіть із project root: python -m unittest discover -s tests -v, потім python -m src.main. Імпортуйте src.main у REPL і підтвердьте, що output не з’явився.

Definition of Done
  • Core імпортується без CLI side effects.
  • Imports абсолютні й не залежать від machine-specific paths.
  • Module names не shadow stdlib.
  • Direction dependencies не створює cycle.
  • П’ять tests і module entry повторно проходять із project root.

Модуль 3 завершено

Після тесту прогрес становитиме 12/58 уроків. Фінальний іспит, PDF, QR та email-сертифікат лишаються заблокованими до 16 модулів і чотирьох прийнятих проєктів. Далі — колекції та їхні контракти.

Методичні джерела

Урок, сценарії, пояснення й вправи створені SEOWORK. Посилання ведуть лише на офіційну документацію Python.

Практична перевірка · урок 12 з 47

Закріпіть матеріал уроку

Три сценарні питання. Для зарахування уроку потрібно дати щонайменше дві правильні відповіді.

1. Після import src.routing caller хоче викликати route_case. Яка форма найкраще показує origin name?
2. Навіщо CLI-виклик main() розміщують під if __name__ == '__main__' у module, який також імпортують tests?
3. Module main імпортує routing, а routing у відповідь імпортує main для constant. Який structural smell виникає?