Spec-Zone.ru › Python 3.12

Модель выполнения

4.1. Структура программы

Программа на Python строится из блоков кода. Блок — это фрагмент текста программы на Python, который выполняется как единое целое. К блокам относятся модуль, тело функции и определение класса. Каждая команда, введённая в интерактивном режиме, является блоком. Файл сценария (файл, переданный интерпретатору в качестве стандартного ввода или указанный как аргумент командной строки интерпретатора) является блоком кода. Команда сценария (команда, указанная в командной строке интерпретатора с параметром -c) является блоком кода. Модуль, запущенный из командной строки как сценарий верхнего уровня (в качестве модуля __main__) с аргументом -m, также является блоком кода. Строковый аргумент, переданный встроенным функциям eval() и exec(), является блоком кода.

Блок кода выполняется в кадре выполнения. Кадр содержит некоторую административную информацию (используемую для отладки) и определяет, где и как продолжится выполнение после завершения блока кода.

4.2. Имена и связывание

4.2.1. Связывание имён

Имена ссылаются на объекты. Имена вводятся операциями связывания имён.

Следующие конструкции связывают имена:

  • формальные параметры функций,
  • определения классов,
  • определения функций,
  • выражения присваивания,
  • цели, являющиеся идентификаторами в присваивании:

    • заголовок цикла for,
    • после as в инструкции with, предложении except, предложении except* или в шаблоне as при структурном сопоставлении с образцом,
    • в шаблоне захвата при структурном сопоставлении с образцом
  • инструкции import.
  • инструкции type.
  • списки параметров типа.

Инструкция import вида from ... import * связывает все имена, определённые в импортированном модуле, кроме имён, начинающихся с подчёркивания. Эту форму можно использовать только на уровне модуля.

Цель в инструкции del также считается связанной для этой цели (хотя фактически семантика состоит в отвязывании имени).

Каждая инструкция присваивания или импорта находится внутри блока, определённого классом или функцией, либо на уровне модуля (в блоке кода верхнего уровня).

Если имя связано в блоке, оно является локальной переменной этого блока, если только оно не объявлено как nonlocal или global. Если имя связано на уровне модуля, оно является глобальной переменной. (Переменные блока кода модуля являются локальными и глобальными.) Если переменная используется в блоке кода, но не определена в нём, она является свободной переменной.

Каждое вхождение имени в тексте программы ссылается на связывание этого имени, установленное следующими правилами разрешения имён.

4.2.2. Разрешение имён

Область видимости определяет видимость имени внутри блока. Если локальная переменная определена в блоке, её область видимости включает этот блок. Если определение находится в блоке функции, область видимости распространяется на любые блоки, содержащиеся в определяющем блоке, если только вложенный блок не вводит для этого имени другое связывание.

При использовании имени в блоке кода оно разрешается с учётом ближайшей охватывающей области видимости. Совокупность всех таких областей, видимых блоку кода, называется окружением блока.

Если имя нигде не найдено, возникает исключение NameError. Если текущая область видимости является областью видимости функции, а имя ссылается на локальную переменную, ещё не связанную со значением в момент использования имени, возникает исключение UnboundLocalError. UnboundLocalError является подклассом NameError.

Если операция связывания имени встречается где-либо внутри блока кода, все использования этого имени в блоке считаются ссылками на текущий блок. Это может привести к ошибкам, если имя используется в блоке до его связывания. Это правило неочевидно. В Python нет объявлений, и операции связывания имён могут встречаться где угодно внутри блока кода. Локальные переменные блока кода можно определить, просмотрев весь текст блока в поисках операций связывания имён. Примеры приведены в ответе FAQ об UnboundLocalError.

Если внутри блока встречается инструкция global, все использования указанных в ней имён ссылаются на связывания этих имён в пространстве имён верхнего уровня. Имена разрешаются в пространстве имён верхнего уровня путём поиска в глобальном пространстве имён (то есть в пространстве имён модуля, содержащего блок кода) и в пространстве имён встроенных имён — пространстве имён модуля builtins. Сначала выполняется поиск в глобальном пространстве имён. Если имена там не найдены, поиск выполняется в пространстве имён встроенных имён. Если имена не найдены и там, в глобальном пространстве имён создаются новые переменные. Инструкция global должна предшествовать всем использованиям перечисленных имён.

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

Инструкция nonlocal заставляет соответствующие имена ссылаться на ранее связанные переменные в ближайшей охватывающей области видимости функции. При компиляции возникает ошибка SyntaxError, если указанное имя не существует ни в одной охватывающей области видимости функции. Параметры типа нельзя повторно связывать с помощью инструкции nonlocal.

Пространство имён модуля автоматически создаётся при первом импорте модуля. Главный модуль сценария всегда называется __main__.

Блоки определений классов и аргументы функций exec() и eval() являются особыми в контексте разрешения имён. Определение класса — это исполняемая инструкция, которая может использовать и определять имена. Эти ссылки подчиняются обычным правилам разрешения имён, за исключением того, что несвязанные локальные переменные ищутся в глобальном пространстве имён. Пространство имён определения класса становится словарём атрибутов класса. Область видимости имён, определённых в блоке класса, ограничена блоком класса; она не распространяется на блоки кода методов. Это относится к включениям и генераторным выражениям, но не к областям видимости аннотаций, имеющим доступ к охватывающим их областям видимости классов. Это означает, что следующий пример завершится ошибкой:

class A:
    a = 42
    b = list(a + i for i in range(10))

Однако следующий пример выполнится успешно:

class A:
    type Alias = Nested
    class Nested: pass

print(A.Alias.__value__)  # <type 'A.Nested'>

4.2.3. Области видимости аннотаций

Списки параметров типа и инструкции type вводят области видимости аннотаций, которые ведут себя во многом как области видимости функций, но имеют некоторые исключения, описанные ниже. В настоящее время аннотации не используют области видимости аннотаций, однако ожидается, что в Python 3.13 они будут их использовать после реализации PEP 649.

Области видимости аннотаций используются в следующих случаях:

  • Списки параметров типа для обобщённых псевдонимов типов.
  • Списки параметров типа для обобщённых функций. Аннотации обобщённой функции выполняются в области видимости аннотаций, а значения по умолчанию и декораторы — нет.
  • Списки параметров типа для обобщённых классов. Базовые классы и аргументы-ключевые слова обобщённого класса выполняются в области видимости аннотаций, а декораторы — нет.
  • Границы и ограничения переменных типа (вычисляются отложенно).
  • Значения псевдонимов типов (вычисляются отложенно).

Области видимости аннотаций отличаются от областей видимости функций следующим:

  • Области видимости аннотаций имеют доступ к пространству имён охватывающего их класса. Если область видимости аннотаций непосредственно находится внутри области видимости класса или внутри другой области видимости аннотаций, непосредственно находящейся внутри области видимости класса, код в области видимости аннотаций может использовать имена, определённые в области видимости класса, как если бы он выполнялся непосредственно в теле класса. Это отличается от обычных функций, определённых внутри классов: они не могут обращаться к именам, определённым в области видимости класса.
  • Выражения в областях видимости аннотаций не могут содержать выражения yield, yield from, await или :=. (Эти выражения допустимы в других областях видимости, вложенных в область видимости аннотаций.)
  • Имена, определённые в областях видимости аннотаций, нельзя повторно связывать инструкциями nonlocal во вложенных областях видимости. Это относится только к параметрам типа, поскольку никакие другие синтаксические элементы, которые могут встречаться в областях видимости аннотаций, не вводят новые имена.
  • Хотя у областей видимости аннотаций есть внутреннее имя, оно не отражается в полном имени объектов, определённых в этой области. Вместо этого значение __qualname__ таких объектов будет таким, как если бы объект был определён в охватывающей области видимости.

Добавлено в версии 3.12: Области видимости аннотаций появились в Python 3.12 в рамках PEP 695.

4.2.4. Отложенное вычисление

Значения псевдонимов типов, созданных с помощью инструкции type, вычисляются отложенно. То же относится к границам и ограничениям переменных типа, созданных с помощью синтаксиса параметров типа. Это означает, что они не вычисляются при создании псевдонима типа или переменной типа. Вместо этого они вычисляются только тогда, когда это необходимо для разрешения доступа к атрибуту.

Пример:

>>> type Alias = 1/0
>>> Alias.__value__
Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero
>>> def func[T: 1/0](): pass
>>> T = func.__type_params__[0]
>>> T.__bound__
Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero

В этом примере исключение возникает только при обращении к атрибуту __value__ псевдонима типа или атрибуту __bound__ переменной типа.

Такое поведение особенно полезно для ссылок на типы, которые ещё не определены в момент создания псевдонима типа или переменной типа. Например, отложенное вычисление позволяет создавать взаимно рекурсивные псевдонимы типов:

from typing import Literal

type SimpleExpr = int | Parenthesized
type Parenthesized = tuple[Literal["("], Expr, Literal[")"]]
type Expr = SimpleExpr | tuple[SimpleExpr, Literal["+", "-"], Expr]

Отложенно вычисляемые значения вычисляются в области видимости аннотаций, поэтому имена внутри такого значения ищутся так, как если бы они использовались в непосредственно охватывающей области видимости.

Добавлено в версии 3.12.

4.2.5. Встроенные имена и ограниченное выполнение

Особенность реализации CPython: Пользователям не следует обращаться к __builtins__; это исключительно внутренняя деталь реализации. Чтобы переопределить значения в пространстве имён встроенных имён, следует import модуль builtins и соответствующим образом изменить его атрибуты.

Пространство имён встроенных имён, связанное с выполнением блока кода, на самом деле определяется поиском имени __builtins__ в его глобальном пространстве имён; это должен быть словарь или модуль (в последнем случае используется словарь модуля). По умолчанию в модуле __main__ имя __builtins__ ссылается на встроенный модуль builtins; в любом другом модуле __builtins__ является псевдонимом словаря самого модуля builtins.

4.2.6. Взаимодействие с динамическими возможностями

Разрешение имён свободных переменных происходит во время выполнения, а не во время компиляции. Это означает, что следующий код выведет 42:

i = 10
def f():
    print(i)
i = 42
f()

Функции eval() и exec() не имеют доступа ко всему окружению, необходимому для разрешения имён. Имена могут разрешаться в локальном и глобальном пространствах имён вызывающего кода. Свободные переменные разрешаются не в ближайшем охватывающем пространстве имён, а в глобальном пространстве имён. [1] Функции exec() и eval() принимают необязательные аргументы для переопределения глобального и локального пространств имён. Если указано только одно пространство имён, оно используется для обоих.

4.3. Исключения

Исключения позволяют выйти из обычного потока управления блоком кода для обработки ошибок или других исключительных ситуаций. Исключение возникает в момент обнаружения ошибки; его может обработать окружающий блок кода или любой блок кода, прямо или косвенно вызвавший блок, в котором произошла ошибка.

Интерпретатор Python вызывает исключение при обнаружении ошибки времени выполнения (например, деления на ноль). Программа на Python также может явно вызвать исключение с помощью инструкции raise. Обработчики исключений задаются инструкцией try … except. Предложение finally такой инструкции можно использовать для указания кода очистки, который не обрабатывает исключение, но выполняется независимо от того, произошло ли исключение в предшествующем коде.

Python использует модель обработки ошибок с «завершением»: обработчик исключения может выяснить, что произошло, и продолжить выполнение на внешнем уровне, но не может устранить причину ошибки и повторить неудачную операцию (кроме как повторно войдя в проблемный фрагмент кода с самого начала).

Если исключение вообще не обрабатывается, интерпретатор завершает выполнение программы или возвращается в интерактивный главный цикл. В обоих случаях он выводит трассировку стека, кроме ситуации, когда исключение имеет тип SystemExit.

Исключения идентифицируются экземплярами классов. Предложение except выбирается в зависимости от класса экземпляра: оно должно ссылаться на класс экземпляра или на его невиртуальный базовый класс. Экземпляр может быть передан обработчику и содержать дополнительную информацию об исключительной ситуации.

Примечание

Тексты сообщений об исключениях не являются частью API Python. Их содержимое может меняться от одной версии Python к другой без предупреждения, поэтому код, рассчитанный на работу с несколькими версиями интерпретатора, не должен на них полагаться.

См. также описание инструкции try в разделе Инструкция try и инструкции raise в разделе Инструкция raise.

Сноски

[1]

Это ограничение существует потому, что код, выполняемый этими операциями, недоступен на момент компиляции модуля.

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

Spec-Zone.ru

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