Самая странная строка в Python, демистифицированная
Открой почти любой Python-файл — и рано или поздно увидишь внизу:
if __name__ == "__main__":
main()
Выглядит как магический бойлерплейт. На деле — вполне прагматичная вещь, если понять, что она делает. Существует потому, что Python не проводит границу между «скриптами» и «библиотеками».
Подоплёка: два способа использовать файл
Любой .py-файл в Python можно использовать двумя способами:
- Как скрипт — запускаешь напрямую:
python3 file.py. - Как модуль — другой файл импортирует его:
import file.
Язык не заставляет тебя заявлять, что именно ты пишешь. Любой файл может в итоге выполнять обе роли.
Рассмотрим файл math_helpers.py:
# math_helpers.py
def mean(values):
return sum(values) / len(values)
def median(values):
s = sorted(values)
mid = len(s) // 2
if len(s) % 2 == 0:
return (s[mid - 1] + s[mid]) / 2
return s[mid]
# Quick smoke test while developing
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print("mean:", mean(numbers))
print("median:", median(numbers))
Запустишь этот файл напрямую — он распечатает тестовый вывод, отлично. Но если другой файл сделает import math_helpers, эти print выполнятся во время импорта. Каждый пользователь модуля увидит твои отладочные распечатки. Уже не так хорошо.
Что такое __name__
У каждого Python-модуля есть встроенная переменная __name__. Python устанавливает её автоматически:
- При импорте файла
__name__принимает имя модуля —"math_helpers"в примере выше. - При прямом запуске файла Python ставит
__name__в специальную строку"__main__".
Это даёт тебе способ понять, в каком режиме ты находишься. Оборачиваешь «только-для-скрипта» код в проверку:
# math_helpers.py
def mean(values):
return sum(values) / len(values)
def median(values):
s = sorted(values)
mid = len(s) // 2
if len(s) % 2 == 0:
return (s[mid - 1] + s[mid]) / 2
return s[mid]
if __name__ == "__main__":
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print("mean:", mean(numbers))
print("median:", median(numbers))
Теперь python3 math_helpers.py печатает тест. А import math_helpers в другом файле полностью пропускает блок — функции доступны, ничего лишнего не выполняется.
Частая пара: функция main()
Большинство реальных скриптов обзаводится отдельной функцией main(), а сам блок-защита остаётся крошечным:
# process_orders.py
import sys
def load_orders(path):
with open(path) as f:
return [line.strip() for line in f if line.strip()]
def process(orders):
for order in orders:
print(f"Processing {order}")
def main():
if len(sys.argv) < 2:
print("Usage: python3 process_orders.py <file>")
sys.exit(1)
orders = load_orders(sys.argv[1])
process(orders)
if __name__ == "__main__":
main()
Зачем заморачиваться с main() вместо того, чтобы впихнуть логику прямо под защиту?
- Тестируемость. Можно импортировать модуль и вызвать
main()(или любой хелпер) из тестов. - Локальные переменные. Всё, что определено внутри
main(), — локальные переменные, а не имена модульного уровня. Это избавляет от случайных коллизий имён и делает публичную поверхность модуля чище. - Читаемость. Блок-защита теперь две строки — ясная «точка входа скрипта», а не куча логики.
Быстрая демонстрация
Поведение можно увидеть напрямую:
Запустив сниппет выше с Python, увидишь __name__ is: __main__ и «being run as a script». Если какой-то другой файл его импортирует — та же проверка напечатает другую ветку.
Когда защита не нужна
Масса Python-файлов никогда её не получает. Короткие скрипты, которые никто не импортирует, нормально живут и без неё:
# one_off_rename.py
import os
for name in os.listdir("."):
if name.endswith(".txt"):
os.rename(name, name.lower())
Это совершенно нормально. Ты не строишь библиотеку; никто это не импортирует. Но как только в файле появляется переиспользуемая логика — и ты хочешь держать рядом код с побочными эффектами — тянись к защите.
Entry points для установленных пакетов
Когда ты публикуешь код как настоящий пакет, есть более формальный механизм: entry_points в метаданных пакета, связывающие команду с функцией. Устанавливая твой пакет, пользователи получают консольную команду, которая напрямую вызывает функцию. Но для скриптов, запускаемых из папки на твоей машине, защита __main__ — стандартный инструмент.
Подводим итог этой главы
Теперь у тебя есть:
- Функции, чтобы дать логике имя.
*args/**kwargsдля гибких вызовов.- Lambda для одноразовых функций.
- Декораторы и аннотации типов, чтобы оборачивать и документировать эти функции.
- Модули, pip и виртуальные окружения, чтобы раскладывать код по файлам и проектам.
- Защита
__main__, чтобы скрипты и модули уживались.
Дальше: классы
До сих пор ты в основном соединял функции с простыми данными — словарями, списками, кортежами. Следующая глава знакомит с классами — способом Python связать данные и поведение и местом, где живут понятия вроде self, __init__, наследования и dataclass-ов.
Часто задаваемые вопросы
Что делает if __name__ == '__main__'?
if __name__ == '__main__'?Блок ниже выполняется только когда файл запущен напрямую (а не когда его импортируют как модуль). Это позволяет написать .py-файл, который можно и запустить как скрипт, и импортировать ради функций и классов — без того, чтобы действия скрипта сработали при импорте.
Нужна ли Python main-функция?
Нет. Python-файлы автоматически выполняются сверху вниз. Но как только у файла появляется логика, которую хочется и импортировать, защита if __name__ == '__main__' становится важной — и вынос работы скрипта в функцию main() — частая сопутствующая привычка.
В чём разница между __name__ и __main__?
__name__ и __main__?__name__ — специальная переменная, которую Python задаёт в каждом модуле. Если файл импортирован, __name__ — это имя модуля (например, 'util'). Если файл запущен напрямую, Python ставит __name__ в строку '__main__'. Проверка if сравнивает эти два значения.