Понимание точки входа в Python: условие __name__ == '__main__'
Конструкция 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)
Это позволяет контролировать поведение модуля в зависимости от способа его использования.
Часто встречающиеся проблемы и ошибки
- Код выполняется при импорте, если забыть конструкцию. Любой код на верхнем уровне (без обёртки) запускается при первом импорте модуля. Решение: всегда помещать исполняемый код внутрь условного блока или функции.
- Оператор сравнения с опечаткой. Например,
if __name__ == "__main__":- верно, аif __name__ == "__main"(без подчёркиваний) не сработает. Всегда проверяйте написание двойных подчёркиваний. - Использование конструкции в модуле
__init__.py. Файл__init__.pyобычно не предназначен для прямого запуска, но если в нём есть блокif __name__ == '__main__', он сработает только при запуске самого__init__.py, что случается редко. Для пакетов лучше создавать отдельный исполняемый файл__main__.py. - Путаница с относительными импортами. При запуске модуля из пакета с помощью
python -m package.moduleпеременная__name__внутри модуля будет равнаpackage.module, а не'__main__'. В таких случаях конструкцияif __name__ == '__main__'не сработает, для точки входа в пакет следует использовать отдельный__main__.py. - Двойной запуск в многопроцессорных приложениях. На 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)