Spec-Zone.ru › Python 3.10

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

Активно следуя этой конвенции, наш модуль будет иметь такое же поведение при запуске напрямую (т. е. 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[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__'. Вы можете вызвать его с помощью 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.10/library/__main__.html

Spec-Zone.ru

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