2.19. Написание модулей

Любой файл .py является модулем. Разбиение растущего скрипта на несколько файлов делает каждый файл коротким и позволяет совместно использовать общие вспомогательные функции между скриптами.

2.19.1. Разбиение скрипта

Вынесите связанную группу функций в отдельный файл:

camera_utils.py:

def banner():
    print("OpenMV")

def label(text):
    return "[" + text + "]"

main.py:

import camera_utils

camera_utils.banner()
print(camera_utils.label("ready"))

Вывод:

OpenMV
[ready]

Эти два файла располагаются рядом в одном каталоге. Когда запускается main.py, import camera_utils один раз читает camera_utils.py, выполняет его операторы верхнего уровня и привязывает получившийся объект модуля к имени camera_utils в main. Повторный import camera_utils откуда-либо ещё возвращает тот же объект – модули кэшируются после первой загрузки.

Имя модуля берётся из имени его файла, поэтому camera_utils.py импортируется как import camera_utils.

2.19.2. Многофайловые модули (пакеты)

Модуль также может быть каталогом файлов, а не одним файлом .py. Имя каталога становится именем модуля, а файлы внутри – его подмодулями:

camera_utils/
    __init__.py
    text.py
    timing.py

__init__.py – это файл, который выполняется при импорте самого пакета; он может быть пустым или может повторно экспортировать выбранные имена из подмодулей. К подмодулям обращаются через имя с точкой:

import camera_utils.text
from camera_utils.timing import elapsed

camera_utils.text.label("ready")

Внутри пакета подмодули могут обращаться друг к другу либо по полному имени с точкой, либо через относительный импорт, использующий ведущую точку для обозначения «этого пакета»:

camera_utils/timing.py:

from . import text             # the sibling submodule

def stamp(value):
    return text.label(str(value))

Чтобы вместо этого подтянуть конкретное имя, назовите его после соседа с точкой:

from .text import label

def stamp(value):
    return label(str(value))

Относительные импорты делают пакет самодостаточным: переименование каталога пакета не требует редактирования каждого подмодуля.

Используйте пакет, когда один файл вырастает за пределы удобного размера или когда набор связанных модулей должен находиться вместе под одним пространством имён. Для повседневных скриптов достаточно одного файла .py.

2.19.3. Защита через __name__

У каждого модуля есть встроенное имя __name__. Его значение зависит от того, как используется файл:

  • Когда файл запускается напрямую, __name__ устанавливается в строку "__main__".

  • Когда файл импортируется другим скриптом, __name__ устанавливается в имя модуля – имя файла без .py.

Идиома, которая это использует:

label_util.py:

def label(text):
    return "[" + text + "]"

if __name__ == "__main__":
    print(label("self-test"))

Вывод (при запуске напрямую):

[self-test]

Когда тот же файл вместо этого импортируется, __name__ устанавливается в имя модуля, поэтому блок if пропускается и ничего лишнего не выполняется:

>>> import label_util
>>> label_util.__name__
'label_util'

Используйте этот приём, чтобы прикрепить быстрый дымовой тест или демонстрацию к файлу библиотеки, не нарушая работу скриптов, которые его импортируют.