__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 в качестве аргумента файла:

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

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

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

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

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

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

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).

Если мы сами будем активно следовать этому соглашению, наш модуль будет вести себя так же при непосредственном запуске (т.е. python3 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. Например:

$ python3 -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, упомянутые ниже.

См.также

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

Смотрите runpy для получения более подробной информации о флаге -m для исполняемого файла интерпретатора.

Смотрите zipapp, чтобы узнать, как запускать приложения, упакованные в виде файлов .zip. В этом случае 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())

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

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

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

$ python3 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.

Вернуться на верх