Понимание точки входа в Python: условие __name__ == '__main__'

Раздел: Основы Python -> Модули и импорт

Конструкция if __name__ == '__main__' в Python

В Python переменная __name__ автоматически получает значение '__main__', когда файл запускается напрямую как скрипт. Если же файл импортируется как модуль, __name__ принимает имя этого модуля. Конструкция if __name__ == '__main__': позволяет определить, исполняется ли код как основная программа или как импортированный модуль, и выполнять разную логику в зависимости от ситуации.

Как правильно организовать точку входа в скрипт?

Наиболее распространённый и рекомендуемый подход - поместить основной код в функцию (обычно main()) и вызвать её внутри условия:

def main():
    print("Скрипт запущен напрямую")
    # здесь располагается основная логика

if __name__ == '__main__':
    main()

If name main python (конструкция if __name__ == '__main__' в python)

Такой подход позволяет импортировать модуль без выполнения побочных эффектов, а функцию main() можно вызывать в тестах или из других модулей.

Вариант 1. Как выполнить код без создания функции?

Если скрипт очень короткий, можно обойтись без отдельной функции, поместив код прямо в блок условия:

# simple_script.py
if __name__ == '__main__':
    print("Простой запуск")
    result = 2 + 2
    print(f"Результат: {result}")

Python импорт переменной (импорт переменной из другого модуля в python)

Недостаток: код не переиспользуется при импорте, сложнее тестировать отдельные части.

Вариант 2. Как запустить один и тот же модуль и как скрипт, и как импортируемую библиотеку?

В блоке условия можно разместить не только вызов функций, но и запуск системного тестирования (например, doctest или unitest):

# my_math.py
def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

if __name__ == '__main__':
    # Здесь можно выполнять тесты или демонстрационные вызовы
    print("Тестирование функций модуля:")
    print(f"add(2,3) = {add(2,3)}")
    print(f"subtract(10,4) = {subtract(10,4)}")

импорт файла python (импорт файла (модуля) в python)

При импорте my_math тесты не запустятся, а функции будут доступны.

Вариант 3. Как создать утилиту командной строки с аргументами?

Конструкция идеально сочетается с argparse или sys.argv:

# greeter.py
import sys

def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

if __name__ == '__main__':
    if len(sys.argv) > 1:
        name = sys.argv[1]
        greeting = sys.argv[2] if len(sys.argv) > 2 else "Hello"
        print(greet(name, greeting))
    else:
        print("Usage: python greeter.py  [greeting]")

Теперь скрипт можно вызывать из командной строки:
$ python greeter.py Alice Hi
а функции greet() можно импортировать в другие проекты.

Вариант 4. Как выполнять разный код при разработке и в продакшене?

Иногда нужно запустить профилирование, логирование или дебаг-режим только при прямом запуске:

# app.py
import logging

def run_app(config):
    # основная логика приложения
    pass

if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG)
    run_app({'debug': True})
else:
    # при импорте используется менее детальный лог
    logging.basicConfig(level=logging.WARNING)

Это позволяет контролировать поведение модуля в зависимости от способа его использования.

Часто встречающиеся проблемы и ошибки

  1. Код выполняется при импорте, если забыть конструкцию. Любой код на верхнем уровне (без обёртки) запускается при первом импорте модуля. Решение: всегда помещать исполняемый код внутрь условного блока или функции.
  2. Оператор сравнения с опечаткой. Например, if __name__ == "__main__": - верно, а if __name__ == "__main" (без подчёркиваний) не сработает. Всегда проверяйте написание двойных подчёркиваний.
  3. Использование конструкции в модуле __init__.py. Файл __init__.py обычно не предназначен для прямого запуска, но если в нём есть блок if __name__ == '__main__', он сработает только при запуске самого __init__.py, что случается редко. Для пакетов лучше создавать отдельный исполняемый файл __main__.py.
  4. Путаница с относительными импортами. При запуске модуля из пакета с помощью python -m package.module переменная __name__ внутри модуля будет равна package.module, а не '__main__'. В таких случаях конструкция if __name__ == '__main__' не сработает, для точки входа в пакет следует использовать отдельный __main__.py.
  5. Двойной запуск в многопроцессорных приложениях. На Windows при использовании multiprocessing каждый новый процесс заново импортирует модуль. Если защита не предусмотрена, код внутри if __name__ == '__main__' может выполниться повторно в каждом подпроцессе. Стандартная практика - оборачивать вызов multiprocessing.Process также внутрь этого условия.

Расширенные примеры использования if __name__ == '__main__'

Пример 1. Пакет с точкой входа через __main__.py

Когда проект оформлен как пакет (директория с __init__.py), удобно создать файл __main__.py, который запускается командой python -m package_name. В этом файле используется та же конструкция:

Пример
# структура проекта:
# mypackage/
#   __init__.py
#   __main__.py
#   core.py

# __main__.py
from mypackage.core import run

if __name__ == '__main__':
    run()
$ python -m mypackage
 # Результат: выполнение функции run()

Пример 2. Модульное тестирование с doctest

Конструкция позволяет встраивать тесты прямо в документацию и запускать их только при прямом запуске модуля:

Пример
# calculator.py
def multiply(a, b):
    """
    Возвращает произведение a и b.

    >>> multiply(3, 4)
    12
    >>> multiply(-1, 5)
    -5
    """
    return a * b

def divide(a, b):
    """
    Возвращает частное a и b.

    >>> divide(10, 2)
    5.0
    >>> divide(7, 0)
    Traceback (most recent call last):
        ...
    ZeroDivisionError: division by zero
    """
    if b == 0:
        raise ZeroDivisionError("division by zero")
    return a / b

if __name__ == '__main__':
    import doctest
    doctest.testmod(verbose=True)
$ python calculator.py
Trying:
    multiply(3, 4)
Expecting:
    12
ok
Trying:
    multiply(-1, 5)
Expecting:
    -5
ok
...
1 items passed all tests:
   2 tests in __main__.multiply
...
Test passed.

Пример 3. Асинхронный запуск с asyncio

При написании асинхронных скриптов точка входа должна содержать вызов asyncio.run():

Пример
# async_demo.py
import asyncio

async def fetch_data(url):
    # имитация загрузки
    await asyncio.sleep(1)
    return f"Данные из {url}"

async def main():
    urls = ["http://example.com", "http://python.org"]
    tasks = [fetch_data(url) for url in urls]
    results = await asyncio.gather(*tasks)
    print("Результаты:", results)

if __name__ == '__main__':
    asyncio.run(main())
$ python async_demo.py
Результаты: ['Данные из http://example.com', 'Данные из http://python.org']

Пример 4. Использование с флагами командной строки (argparse)

Современный способ создания CLI-утилит с помощью модуля argparse:

Пример
# cli_tool.py
import argparse

def process_file(filename, uppercase=False):
    with open(filename, 'r') as f:
        content = f.read()
    return content.upper() if uppercase else content

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Обработчик файлов")
    parser.add_argument('filename', help="Имя файла для обработки")
    parser.add_argument('--uppercase', '-u', action='store_true',
                        help="Преобразовать в верхний регистр")
    args = parser.parse_args()

    result = process_file(args.filename, args.uppercase)
    print(result)
$ python cli_tool.py test.txt --uppercase
# выведет содержимое test.txt в верхнем регистре

Пример 5. Профилирование по запросу

Иногда полезно включать профилирование только при прямом запуске, сохраняя чистоту при импорте:

Пример
# optimized.py
import cProfile

def compute_heavy():
    # ресурсоёмкая операция
    total = 0
    for i in range(1000000):
        total += i ** 2
    return total

if __name__ == '__main__':
    cProfile.run('compute_heavy()', sort='cumulative')
$ python optimized.py
         4 function calls in 0.123 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.123    0.123    0.123    0.123 optimized.py:8(compute_heavy)
        1    0.000    0.000    0.123    0.123 {built-in method builtins.exec}
        1    0.000    0.000    0.000    0.000 {method 'disable' ...}

Пример 6. Разделение тестов и рабочего кода в одном файле

Для небольших проектов можно совмещать реализацию и тесты в одном модуле, защищая тесты условием:

Пример
# validator.py
def validate_email(email):
    import re
    pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
    return bool(re.match(pattern, email))

# Тесты, выполняемые только при прямом запуске
if __name__ == '__main__':
    # Простые тесты без внешних фреймворков
    tests = [
        ("user@example.com", True),
        ("user@domain", False),
        ("@example.com", False),
        ("", False),
    ]
    for email, expected in tests:
        result = validate_email(email)
        status = "✅" if result == expected else "❌"
        print(f"{status} validate_email('{email}') = {result} (expected {expected})")
$ python validator.py
✅ validate_email('user@example.com') = True (expected True)
✅ validate_email('user@domain') = False (expected False)
✅ validate_email('@example.com') = False (expected False)
✅ validate_email('') = False (expected False)

Конструкция if __name__ == '__main__' в Python - comments

En
If name main python (python)