__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 (которое неявно возвращается, если ваша функция не имеет оператора возврата).

Упреждающе следуя этому соглашению, наш модуль будет иметь такое же поведение при прямом запуске (т.е. 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[2] 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__'. Вы можете вызвать его с помощью python3 -m venv [directory].

Более подробную информацию о флаге runpy для исполняемого интерпретатора см. в разделе -m.

О том, как запускать приложения, упакованные в файлы .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())

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

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

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