Spec-Zone.ru › Python 3.11

__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__, что позволяет использовать общую конструкцию для условного выполнения кода, когда модуль не инициализирован из оператора импорта:

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

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

Spec-Zone.ru

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