Spec-Zone.ru › Python 3.12

__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 содержит подборку учебных материалов и справочных сведений о распространении и установке пакетов 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 без проверки __name__.

См. также

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

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

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

import __main__

Независимо от того, с какого модуля была запущена программа Python, другие модули, выполняющиеся в этой же программе, могут импортировать область видимости (пространство имён) среды верхнего уровня, импортировав модуль __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. Подробнее об этом см. в справочнике по системе импорта, раздел Особые случаи, связанные с __main__.

Ещё один пример «среды верхнего уровня» — интерактивная оболочка Python 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.

© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/__main__.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API