> **Источник:** https://python-all.ru/3.11/library/__main__.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# [`__main__`](https://python-all.ru/3.11/library/__main__.html#module-__main__) – Среда кода верхнего уровня

---

В Python специальное имя `__main__` используется для двух важных конструкций:

1. имя среды верхнего уровня программы, которое можно проверить с помощью выражения `__name__ == '__main__'`; и
2. файл `__main__.py` в пакетах Python.

Оба этих механизма связаны с модулями Python: как пользователи взаимодействуют с ними и как модули взаимодействуют друг с другом. Они подробно описаны ниже. Если вы новичок в работе с модулями Python, см. раздел учебного пособия [Модули](https://python-all.ru/3.11/tutorial/modules.html#tut-modules) для введения.

## `__name__ == '__main__'`

Когда модуль или пакет Python импортируется, `__name__` устанавливается в имя модуля. Обычно это имя самого файла Python без расширения `.py`:

```python
>>> import configparser
>>> configparser.__name__
'configparser'
```

Если файл является частью пакета, `__name__` также будет включать путь родительского пакета:

```python
>>> from concurrent.futures import process
>>> process.__name__
'concurrent.futures.process'
```

Однако если модуль выполняется в среде кода верхнего уровня, его `__name__` устанавливается в строку `'__main__'`.

### Что такое «среда кода верхнего уровня»?

`__main__` – это имя среды, в которой выполняется код верхнего уровня. «Код верхнего уровня» – это первый указанный пользователем модуль Python, который запускается. Он называется «верхнего уровня», потому что импортирует все остальные модули, необходимые программе. Иногда «код верхнего уровня» называют *точкой входа* в приложение.

Средой кода верхнего уровня может быть:

- область интерактивного приглашения:

  ```python
  >>> __name__
  '__main__'
  ```
- модуль Python, переданный интерпретатору Python в качестве аргумента-файла:

  ```console
  $ python3 helloworld.py
  Hello, world!
  ```
- модуль или пакет Python, переданные интерпретатору Python с аргументом [`-m`](https://python-all.ru/3.11/using/cmdline.html#cmdoption-m):

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

  ```console
  $ echo "import this" | python3
  The Zen of Python, by Tim Peters

  Beautiful is better than ugly.
  Explicit is better than implicit.
  ...
  ```
- код Python, переданный интерпретатору Python с аргументом [`-c`](https://python-all.ru/3.11/using/cmdline.html#cmdoption-c):

  ```console
  $ python3 -c "import this"
  The Zen of Python, by Tim Peters

  Beautiful is better than ugly.
  Explicit is better than implicit.
  ...
  ```

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

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

```python
if __name__ == '__main__':
    # Выполняется, когда модуль не инициализирован из оператора import.
    ...
```

> **См. также**
>
> Более подробно о том, как `__name__` устанавливается во всех ситуациях, см. в разделе учебного пособия [Модули](https://python-all.ru/3.11/tutorial/modules.html#tut-modules).

### Идиоматическое использование

Некоторые модули содержат код, предназначенный только для использования в скриптах, например разбор аргументов командной строки или получение данных из стандартного ввода. Если такой модуль импортируется из другого модуля, например для модульного тестирования, код скрипта также непреднамеренно выполнится.

Здесь пригождается использование блока кода `if __name__ == '__main__'`. Код внутри этого блока не будет выполняться, пока модуль не будет запущен в среде верхнего уровня.

Размещение как можно меньшего количества операторов в блоке ниже `if __name__ == '__main__'` может улучшить ясность и корректность кода. Чаще всего функция с именем `main` инкапсулирует основное поведение программы:

```python
# echo.py

import shlex
import sys

def echo(phrase: str) -> None:
   """Пустая обёртка вокруг print."""
   # для целей демонстрации можно представить, что существует некоторая
   # ценная и повторно используемая логика внутри этой функции
   print(phrase)

def main() -> int:
    """Выводит входные аргументы в стандартный вывод"""
    phrase = shlex.join(sys.argv)
    echo(phrase)
    return 0

if __name__ == '__main__':
    sys.exit(main())  # в следующем разделе объясняется использование sys.exit
```

Обратите внимание: если бы модуль не инкапсулировал код внутри функции `main`, а поместил бы его непосредственно в блок `if __name__ == '__main__'`, переменная `phrase` была бы глобальной для всего модуля. Это чревато ошибками, так как другие функции в модуле могли бы случайно использовать глобальную переменную вместо локального имени. Функция `main` решает эту проблему.

Использование функции `main` даёт дополнительное преимущество: сама функция `echo` изолируется и может быть импортирована в другом месте. При импорте `echo.py` функции `echo` и `main` будут определены, но ни одна из них не будет вызвана, потому что `__name__ != '__main__'`.

### Особенности упаковки

Функции `main` часто используются для создания инструментов командной строки путём указания их в качестве точек входа для консольных скриптов. Когда это делается, [pip](https://python-all.ru/3.11/library/__main__.html) вставляет вызов функции в шаблонный скрипт, где возвращаемое значение `main` передаётся в [`sys.exit()`](https://python-all.ru/3.11/library/sys.html#sys.exit). Например:

```python
sys.exit(main())
```

Поскольку вызов `main` обёрнут в [`sys.exit()`](https://python-all.ru/3.11/library/sys.html#sys.exit), ожидается, что функция вернёт значение, приемлемое в качестве ввода для [`sys.exit()`](https://python-all.ru/3.11/library/sys.html#sys.exit); обычно это целое число или `None` (которое неявно возвращается, если в функции нет оператора return).

Следуя этому соглашению заранее, модуль будет вести себя одинаково при прямом запуске (т.е. `python3 echo.py`) и при последующей упаковке в качестве точки входа консольного скрипта в пакет, устанавливаемый через pip.

В частности, будьте осторожны с возвратом строк из функции `main`. [`sys.exit()`](https://python-all.ru/3.11/library/sys.html#sys.exit) интерпретирует строковый аргумент как сообщение об ошибке, поэтому программа получит код возврата `1`, указывающий на сбой, а строка будет записана в [`sys.stderr`](https://python-all.ru/3.11/library/sys.html#sys.stderr). Пример `echo.py` из предыдущего демонстрирует использование соглашения `sys.exit(main())`.

> **См. также**
>
> [Руководство пользователя по упаковке Python](https://python-all.ru/3.11/library/__main__.html) содержит набор учебных пособий и справочных материалов о том, как распространять и устанавливать пакеты Python с помощью современных инструментов.

## `__main__.py` в пакетах Python

Если вы не знакомы с пакетами Python, обратитесь к разделу [Пакеты](https://python-all.ru/3.11/tutorial/modules.html#tut-packages) учебного пособия. Чаще всего файл `__main__.py` используется для предоставления интерфейса командной строки для пакета. Рассмотрим следующий гипотетический пакет «bandclass»:

```text
bandclass
  ├── __init__.py
  ├── __main__.py
  └── student.py
```

`__main__.py` будет выполнен, когда сам пакет вызывается напрямую из командной строки с помощью флага [`-m`](https://python-all.ru/3.11/using/cmdline.html#cmdoption-m). Например:

```console
$ python3 -m bandclass
```

Эта команда запустит `__main__.py`. То, как вы используете этот механизм, зависит от характера пакета, который вы пишете, но в этом гипотетическом случае может иметь смысл позволить учителю искать учеников:

```python
# 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` – это пример относительного импорта. Этот стиль импорта можно использовать при обращении к модулям внутри пакета. Подробнее см. [Ссылки внутри пакета](https://python-all.ru/3.11/tutorial/modules.html#intra-package-references) в разделе [Модули](https://python-all.ru/3.11/tutorial/modules.html#tut-modules) учебного пособия.

### Идиоматическое использование

Содержимое `__main__.py` обычно не обрамляется блоком `if __name__ == '__main__'`. Вместо этого такие файлы делают короткими и импортируют функции для выполнения из других модулей. Эти другие модули затем можно легко модульно тестировать, и они надлежащим образом пригодны для повторного использования.

Если используется, блок `if __name__ == '__main__'` всё равно будет работать как ожидается для файла `__main__.py` внутри пакета, потому что его атрибут `__name__` будет включать путь к пакету при импорте:

```python
>>> import asyncio.__main__
>>> asyncio.__main__.__name__
'asyncio.__main__'
```

Однако это не будет работать для файлов `__main__.py` в корневом каталоге .zip-файла. Поэтому для единообразия предпочтительны минимальные `__main__.py`, например [`venv`](https://python-all.ru/3.11/library/venv.html#module-venv), упомянутые ниже.

> **См. также**
>
> См. [`venv`](https://python-all.ru/3.11/library/venv.html#module-venv) для примера пакета с минимальным `__main__.py` в стандартной библиотеке. Он не содержит блока `if __name__ == '__main__'`. Вы можете вызвать его с помощью `python -m venv [directory]`.
>
> См. [`runpy`](https://python-all.ru/3.11/library/runpy.html#module-runpy) для получения более подробной информации о флаге [`-m`](https://python-all.ru/3.11/using/cmdline.html#cmdoption-m) исполняемого файла интерпретатора.
>
> См. [`zipapp`](https://python-all.ru/3.11/library/zipapp.html#module-zipapp) о том, как запускать приложения, упакованные как файлы *.zip*. В этом случае Python ищет файл `__main__.py` в корневом каталоге архива.

## `import __main__`

Независимо от того, с какого модуля была запущена программа Python, другие модули, работающие в той же программе, могут импортировать область видимости верхнего уровня ([пространство имён](https://python-all.ru/3.11/glossary.html#term-namespace)), импортировав модуль `__main__`. При этом импортируется не файл `__main__.py`, а тот модуль, который получил специальное имя `'__main__'`.

Вот пример модуля, который использует пространство имён `__main__`:

```python
# 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)
```

Пример использования этого модуля может быть следующим:

```python
# 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())
```

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

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

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

```console
$ python3 start.py
Dinsdale found in file /path/to/start.py
```

Обратите внимание, что импорт `__main__` не вызывает проблем с непреднамеренным запуском кода верхнего уровня, предназначенного для использования в сценариях, который помещён в блок `if __name__ == "__main__"` модуля `start`. Почему это работает?

Python вставляет пустой модуль `__main__` в [`sys.modules`](https://python-all.ru/3.11/library/sys.html#sys.modules) при запуске интерпретатора и заполняет его, выполняя код верхнего уровня. В нашем примере это модуль `start`, который выполняется строка за строкой и импортирует `namely`. В свою очередь, `namely` импортирует `__main__` (что на самом деле является `start`). Это циклический импорт! К счастью, поскольку частично заполненный модуль `__main__` присутствует в [`sys.modules`](https://python-all.ru/3.11/library/sys.html#sys.modules), Python передаёт его в `namely`. См. [Особые соображения для \_\_main\_\_](https://python-all.ru/3.11/reference/import.html#import-dunder-main) в справочнике по системе импорта для подробностей о том, как это работает.

REPL Python – это ещё один пример «среды верхнего уровня», поэтому всё, определённое в REPL, становится частью области видимости `__main__`:

```python
>>> 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`](https://python-all.ru/3.11/library/pdb.html#module-pdb) и [`rlcompleter`](https://python-all.ru/3.11/library/rlcompleter.html#module-rlcompleter).
