Menu
Русский

Python if __name__ == '__main__': main-защита, разобранная по строчкам

Что на самом деле делает if __name__ == '__main__', почему каждый серьёзный Python-скрипт дорастает до этого паттерна и как сделать файл и скриптом, и модулем одновременно.

Самая странная строка в Python, демистифицированная

Открой почти любой Python-файл — и рано или поздно увидишь внизу:

if __name__ == "__main__":
    main()

Выглядит как магический бойлерплейт. На деле — вполне прагматичная вещь, если понять, что она делает. Существует потому, что Python не проводит границу между «скриптами» и «библиотеками».

Подоплёка: два способа использовать файл

Любой .py-файл в Python можно использовать двумя способами:

  1. Как скрипт — запускаешь напрямую: python3 file.py.
  2. Как модуль — другой файл импортирует его: 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() вместо того, чтобы впихнуть логику прямо под защиту?

  1. Тестируемость. Можно импортировать модуль и вызвать main() (или любой хелпер) из тестов.
  2. Локальные переменные. Всё, что определено внутри main(), — локальные переменные, а не имена модульного уровня. Это избавляет от случайных коллизий имён и делает публичную поверхность модуля чище.
  3. Читаемость. Блок-защита теперь две строки — ясная «точка входа скрипта», а не куча логики.

Быстрая демонстрация

Поведение можно увидеть напрямую:

main.py
Output
Click Run to see the output here.

Запустив сниппет выше с 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__'?

Блок ниже выполняется только когда файл запущен напрямую (а не когда его импортируют как модуль). Это позволяет написать .py-файл, который можно и запустить как скрипт, и импортировать ради функций и классов — без того, чтобы действия скрипта сработали при импорте.

Нужна ли Python main-функция?

Нет. Python-файлы автоматически выполняются сверху вниз. Но как только у файла появляется логика, которую хочется и импортировать, защита if __name__ == '__main__' становится важной — и вынос работы скрипта в функцию main() — частая сопутствующая привычка.

В чём разница между __name__ и __main__?

__name__ — специальная переменная, которую Python задаёт в каждом модуле. Если файл импортирован, __name__ — это имя модуля (например, 'util'). Если файл запущен напрямую, Python ставит __name__ в строку '__main__'. Проверка if сравнивает эти два значения.

Учитесь программировать с Coddy

НАЧАТЬ