__main__ — Среда кода верхнего уровня


В Python специальное имя __main__ используется для двух важных конструкций:

  1. имя среды верхнего уровня программы, которое можно проверить с помощью выражения __name__ == '__main__'; и

  2. файл __main__.py в пакетах Python.

Оба этих механизма связаны с модулями Python, с тем, как пользователи взаимодействуют с ними и как они взаимодействуют друг с другом. Они подробно описаны ниже. Если вы впервые знакомитесь с модулями Python, обратитесь к разделу Модули для введения в курс дела.

__name__ == '__main__'

Когда импортируется модуль или пакет Python, __name__ устанавливается в имя модуля. Обычно это имя самого файла Python без расширения .py:

>>> import configparser
>>> configparser.__name__
'configparser'

Если файл является частью пакета, __name__ также будет включать путь родительского пакета:

>>> from concurrent.futures import process
>>> process.__name__
'concurrent.futures.process'

Однако если модуль выполняется в среде кода верхнего уровня, его __name__ устанавливается в строку '__main__'.

Что такое «среда кода верхнего уровня»?

__main__ - это имя среды, в которой выполняется код верхнего уровня. «Код верхнего уровня» - это первый пользовательский модуль Python, который начинает выполняться. Он «верхнеуровневый», потому что импортирует все остальные модули, которые нужны программе. Иногда «код верхнего уровня» называют точкой входа в приложение.

Среда кода верхнего уровня может быть:

  • рамки интерактивной подсказки:

    >>> __name__
    '__main__'
    
  • модуль Python, передаваемый интерпретатору Python в качестве аргумента файла:

    $ python helloworld.py
    Hello, world!
    
  • модуль или пакет Python, переданный интерпретатору Python с аргументом -m:

    $ python -m tarfile
    usage: tarfile.py [-h] [-v] (...)
    
  • Код Python, считываемый интерпретатором Python со стандартного ввода:

    $ echo "import this" | python
    The Zen of Python, by Tim Peters
    
    Beautiful is better than ugly.
    Explicit is better than implicit.
    ...
    
  • Код Python, передаваемый интерпретатору Python с аргументом -c:

    $ python -c "import this"
    The Zen of Python, by Tim Peters
    
    Beautiful is better than ugly.
    Explicit is better than implicit.
    ...
    

В каждой из этих ситуаций значение __name__ модуля верхнего уровня устанавливается в '__main__'.

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

if __name__ == '__main__':
    # Execute when the module is not initialized from an import statement.
    ...

См.также

Более подробно о том, как устанавливается __name__ во всех ситуациях, читайте в разделе учебника Модули.

Идиоматическое употребление

Некоторые модули содержат код, предназначенный только для использования в сценариях, например разбор аргументов командной строки или получение данных из стандартного ввода. Если такой модуль импортировать из другого модуля, например, для его модульного тестирования, то код скрипта будет непреднамеренно выполняться также.

Именно здесь пригодится использование блока кода if __name__ == '__main__'. Код в этом блоке не будет выполняться, пока модуль не будет выполнен в среде верхнего уровня.

Размещение как можно меньшего количества операторов в блоке под if __name__ == '__main__' может улучшить ясность и корректность кода. Чаще всего функция с именем main инкапсулирует основное поведение программы:

# echo.py

import shlex
import sys

def echo(phrase: str) -> None:
   """A dummy wrapper around print."""
   # for demonstration purposes, you can imagine that there is some
   # valuable and reusable logic inside this function
   print(phrase)

def main() -> int:
    """Echo the input arguments to standard output"""
    phrase = shlex.join(sys.argv)
    echo(phrase)
    return 0

if __name__ == '__main__':
    sys.exit(main())  # next section explains the use of sys.exit

Обратите внимание, что если бы модуль не инкапсулировал код внутри функции main, а поместил его непосредственно в блок if __name__ == '__main__', то переменная phrase стала бы глобальной для всего модуля. Это чревато ошибками, поскольку другие функции внутри модуля могут непреднамеренно использовать глобальную переменную вместо локального имени. Функция main решает эту проблему.

Использование функции main имеет то преимущество, что сама функция echo изолирована и может быть импортирована в другое место. При импорте echo.py будут определены функции echo и main, но ни одна из них не будет вызвана, потому что __name__ != '__main__'.

Соображения по упаковке

Функции main часто используются для создания инструментов командной строки, указывая их в качестве точек входа для консольных скриптов. При этом pip вставляет вызов функции в шаблонный скрипт, где возвращаемое значение main передается в sys.exit(). Например:

sys.exit(main())

Поскольку вызов main обернут в sys.exit(), предполагается, что ваша функция вернет некоторое значение, приемлемое для входа в sys.exit(); обычно это целое число или None (которое неявно возвращается, если ваша функция не имеет оператора return).

Благодаря тому, что мы сами активно следуем этому соглашению, наш модуль будет иметь такое же поведение при прямом запуске (т.е. python echo.py), как и в случае, если мы позже упакуем его в качестве точки входа консольного скрипта в пакет, устанавливаемый с помощью pip.

В частности, будьте осторожны с возвратом строк из функции main. sys.exit() интерпретирует строковый аргумент как сообщение о неудаче, поэтому ваша программа будет иметь код выхода 1, что означает неудачу, а строка будет записана в sys.stderr. Пример echo.py, приведенный ранее, демонстрирует использование соглашения sys.exit(main()).

См.также

Python Packaging User Guide содержит коллекцию руководств и ссылок о том, как распространять и устанавливать пакеты Python с помощью современных инструментов.

__main__.py в пакетах Python

Если вы не знакомы с пакетами Python, обратитесь к разделу Пакеты этого учебника. Чаще всего файл __main__.py используется для создания интерфейса командной строки для пакета. Рассмотрим следующий гипотетический пакет «bandclass»:

bandclass
  ├── __init__.py
  ├── __main__.py
  └── student.py

__main__.py будет выполняться, когда сам пакет вызывается непосредственно из командной строки с помощью флага -m. Например:

$ python -m bandclass

Эта команда приведет к запуску __main__.py. То, как вы используете этот механизм, зависит от характера пакета, который вы пишете, но в этом гипотетическом случае, возможно, имеет смысл позволить учителю искать учеников:

# bandclass/__main__.py

import sys
from .student import search_students

student_name = sys.argv[1] if len(sys.argv) >= 2 else ''
print(f'Found student: {search_students(student_name)}')

Обратите внимание, что from .student import search_students - это пример относительного импорта. Этот стиль импорта можно использовать при ссылке на модули внутри пакета. Более подробную информацию вы найдете в разделе Внутрипакетные ссылки учебника Модули.

Идиоматическое употребление

Содержимое __main__.py обычно не ограждается блоком if __name__ == '__main__'. Вместо этого эти файлы делаются короткими и импортируют функции для выполнения из других модулей. Эти другие модули легко поддаются модульному тестированию и могут быть использованы повторно.

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

>>> import asyncio.__main__
>>> asyncio.__main__.__name__
'asyncio.__main__'

Однако это не будет работать для файлов __main__.py в корневом каталоге .zip-файла. Поэтому для единообразия предпочтительнее использовать минимальные __main__.py, такие как venv, упомянутый ниже.

См.также

Пример пакета с минимальным __main__.py в стандартной библиотеке см. в venv. Он не содержит блока if __name__ == '__main__'. Вы можете вызвать его с помощью python -m venv [directory].

Подробнее о флаге -m в исполняемом файле интерпретатора см. в разделе runpy.

О том, как запускать приложения, упакованные в файлы .zip, читайте в разделе zipapp. В этом случае Python ищет файл __main__.py в корневом каталоге архива.

import __main__

Независимо от того, с каким модулем была запущена программа на Python, другие модули, работающие внутри этой же программы, могут импортировать область видимости среды верхнего уровня (namespace), импортируя модуль __main__. При этом импортируется не файл __main__.py, а тот модуль, который получил специальное имя '__main__'.

Вот пример модуля, который использует пространство имен __main__:

# namely.py

import __main__

def did_user_define_their_name():
    return 'my_name' in dir(__main__)

def print_user_name():
    if not did_user_define_their_name():
        raise ValueError('Define the variable `my_name`!')

    if '__file__' in dir(__main__):
        print(__main__.my_name, "found in file", __main__.__file__)
    else:
        print(__main__.my_name)

Пример использования этого модуля может быть следующим:

# start.py

import sys

from namely import print_user_name

# my_name = "Dinsdale"

def main():
    try:
        print_user_name()
    except ValueError as ve:
        return str(ve)

if __name__ == "__main__":
    sys.exit(main())

Теперь, если мы запустим нашу программу, результат будет выглядеть следующим образом:

$ python start.py
Define the variable `my_name`!

Код выхода программы будет равен 1, что означает ошибку. Отмена комментирования строки с my_name = "Dinsdale" исправляет программу, и теперь она выходит с кодом состояния 0, что означает успех:

$ python start.py
Dinsdale found in file /path/to/start.py

Обратите внимание, что импорт __main__ не вызывает никаких проблем с непреднамеренным запуском кода верхнего уровня, предназначенного для использования в скриптах, который помещен в блок if __name__ == "__main__" модуля start. Почему это работает?

При запуске интерпретатора Python вставляет пустой модуль __main__ в sys.modules и заполняет его, выполняя код верхнего уровня. В нашем примере это модуль start, который запускается строка за строкой и импортирует namely. В свою очередь, namely импортирует __main__ (который на самом деле является start). Это цикл импорта! К счастью, поскольку частично заполненный модуль __main__ присутствует в sys.modules, Python передает его в namely. Подробнее о том, как это работает, читайте в справке по системе импорта Special considerations for __main__.

Python REPL - это еще один пример «среды верхнего уровня», поэтому все, что определено в REPL, становится частью области __main__:

>>> import namely
>>> namely.did_user_define_their_name()
False
>>> namely.print_user_name()
Traceback (most recent call last):
...
ValueError: Define the variable `my_name`!
>>> my_name = 'Jabberwocky'
>>> namely.did_user_define_their_name()
True
>>> namely.print_user_name()
Jabberwocky

Обратите внимание, что в этом случае область видимости __main__ не содержит атрибута __file__, поскольку она интерактивна.

Область __main__ используется в реализации pdb и rlcompleter.