4. Модель выполнения¶
4.1. Структура программы¶
Программа на Python строится из блоков кода. Блок block - это часть текста программы на Python, которая выполняется как единое целое. Блоками являются: модуль, тело функции и определение класса. Каждая команда, набранная в интерактивном режиме, является блоком. Файл сценария (файл, переданный интерпретатору в качестве стандартного ввода или указанный интерпретатору в качестве аргумента командной строки) является блоком кода. Команда скрипта (команда, указанная в командной строке интерпретатора с опцией -c
) - это блок кода. Модуль, запускаемый как скрипт верхнего уровня (как модуль __main__
) из командной строки с помощью аргумента -m
, также является блоком кода. Строковый аргумент, передаваемый встроенным функциям eval()
и exec()
, является блоком кода.
Блок кода выполняется в execution frame. Фрейм содержит некоторую административную информацию (используется для отладки) и определяет, где и как продолжается выполнение после завершения выполнения блока кода.
4.2. Именование и связывание¶
4.2.1. Привязка имен¶
Names относятся к объектам. Имена вводятся с помощью операций связывания имен.
Следующие конструкции связывают имена:
формальные параметры функций,
определения классов,
определения функций,
выражения присвоения,
targets, которые являются идентификаторами, если встречаются в присваивании:
import
заявления.type
заявления.
Оператор import
в форме from ... import *
связывает все имена, определенные в импортируемом модуле, кроме тех, которые начинаются с подчеркивания. Эта форма может использоваться только на уровне модуля.
Цель, встречающаяся в операторе del
, также считается связанной для этой цели (хотя на самом деле семантика заключается в отвязывании имени).
Каждый оператор присваивания или импорта выполняется в блоке, определенном определением класса или функции, или на уровне модуля (блок кода верхнего уровня).
Если имя связано в блоке, оно является локальной переменной этого блока, если только не объявлено как nonlocal
или global
. Если имя связано на уровне модуля, оно является глобальной переменной. (Переменные блока кода модуля являются локальными и глобальными). Если переменная используется в блоке кода, но не определена в нем, она является free variable.
Каждое появление имени в тексте программы относится к binding этого имени, установленному следующими правилами разрешения имен.
4.2.2. Разрешение имен¶
Знак scope определяет видимость имени в блоке. Если локальная переменная определена в блоке, ее область видимости включает этот блок. Если определение происходит в функциональном блоке, то область видимости распространяется на все блоки, содержащиеся в определяющем блоке, если только содержащийся блок не вводит другую привязку для имени.
Когда имя используется в блоке кода, оно разрешается с помощью ближайшей объемлющей области видимости. Набор всех таких областей видимых для блока кода называется environment блока.
Если имя вообще не найдено, возникает исключение NameError
. Если текущая область видимости является областью видимости функции, а имя ссылается на локальную переменную, которая еще не привязана к значению в точке, где используется это имя, то возникает исключение UnboundLocalError
. UnboundLocalError
является подклассом NameError
.
Если операция связывания имени происходит в любом месте блока кода, все использования имени в блоке рассматриваются как ссылки на текущий блок. Это может привести к ошибкам, когда имя используется внутри блока до его связывания. Это правило очень тонкое. В Python нет деклараций, и операции связывания имен могут происходить в любом месте блока кода. Локальные переменные блока кода можно определить, просканировав весь текст блока на предмет операций связывания имен. Примеры см. в разделе the FAQ entry on UnboundLocalError.
Если оператор global
встречается внутри блока, то все использования имен, указанных в этом операторе, относятся к привязкам этих имен в пространстве имен верхнего уровня. Имена разрешаются в пространстве имен верхнего уровня путем поиска в глобальном пространстве имен, то есть в пространстве имен модуля, содержащего блок кода, и в пространстве имен builtins, пространстве имен модуля builtins
. Сначала выполняется поиск в глобальном пространстве имен. Если имена в нем не найдены, то следующим ищется пространство имен builtins. Если имена не найдены и в пространстве имен builtins, то новые переменные создаются в глобальном пространстве имен. Утверждение global должно предшествовать всем случаям использования перечисленных имен.
Оператор global
имеет ту же область видимости, что и операция связывания имен в том же блоке. Если ближайшая окружающая область видимости свободной переменной содержит глобальный оператор, свободная переменная рассматривается как глобальная.
Оператор nonlocal
заставляет соответствующие имена ссылаться на ранее связанные переменные в ближайшей объемлющей области видимости функции. Утверждение SyntaxError
возникает во время компиляции, если данное имя не существует ни в одной из объемлющих функций. Оператор Type parameters не может быть восстановлен с помощью оператора nonlocal
.
Пространство имен для модуля создается автоматически при первом импорте модуля. Главный модуль скрипта всегда называется __main__
.
Блоки определения классов и аргументы к exec()
и eval()
являются специальными в контексте разрешения имен. Определение класса - это исполняемый оператор, который может использовать и определять имена. Эти ссылки следуют обычным правилам разрешения имен, за исключением того, что несвязанные локальные переменные ищутся в глобальном пространстве имен. Пространство имен определения класса становится словарем атрибутов класса. Область видимости имен, определенных в блоке класса, ограничена блоком класса; она не распространяется на блоки кода методов. Сюда входят осмысления и генераторы выражений, но не входят annotation scopes, которые имеют доступ к своим объемлющим областям классов. Это означает, что следующие действия будут неудачными:
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 parameter lists и type
вводят диапазоны аннотаций, которые ведут себя в основном как диапазоны функций, но с некоторыми исключениями, о которых речь пойдет ниже. Операторы Annotations в настоящее время не используют диапазоны аннотаций, но ожидается, что они будут использовать диапазоны аннотаций в Python 3.13, когда будет реализован PEP 649.
Области аннотаций используются в следующих контекстах:
Списки параметров типа для generic type aliases.
Списки параметров типа generic functions. Аннотации общей функции выполняются в области действия аннотации, но ее параметры по умолчанию и декораторы - нет.
Списки параметров типа для generic classes. Базовые классы и аргументы ключевых слов общего класса выполняются в области действия аннотации, а его декораторы - нет.
Границы, ограничения и значения по умолчанию для параметров типа (lazily evaluated).
Значение псевдонимов типов (lazily evaluated).
Области аннотаций отличаются от областей функций следующим образом:
Области аннотаций имеют доступ к окружающему их пространству имен класса. Если аннотационная область находится непосредственно в области видимости класса или в другой аннотационной области, которая находится непосредственно в области видимости класса, то код в аннотационной области может использовать имена, определенные в области видимости класса, как если бы он выполнялся непосредственно в теле класса. Это отличается от обычных функций, определенных внутри классов, которые не могут обращаться к именам, определенным в области видимости класса.
Выражения в диапазонах аннотаций не могут содержать выражения
yield
,yield from
,await
или:=
. (Эти выражения разрешены в других диапазонах, содержащихся в аннотационном диапазоне).Имена, определенные в диапазонах аннотаций, не могут быть восстановлены с помощью операторов
nonlocal
во внутренних диапазонах. Это касается только параметров типа, поскольку никакие другие синтаксические элементы, которые могут появляться в аннотационных диапазонах, не могут вводить новые имена.Хотя аннотационные области имеют внутреннее имя, это имя не отражается в __qualname__ объектов, определенных в этой области. Вместо этого
__qualname__
таких объектов будет таким, как если бы объект был определен в объемлющей области видимости.
Added in version 3.12: Области аннотаций были введены в Python 3.12 как часть PEP 695.
Изменено в версии 3.13: Области аннотаций также используются для параметров типа по умолчанию, как это было введено в PEP 696.
4.2.4. Ленивая оценка¶
Значения псевдонимов типов, созданных с помощью оператора type
, оцениваются однозначно. То же самое относится к границам, ограничениям и значениям по умолчанию переменных типов, созданных с помощью оператора type parameter syntax. Это означает, что они не оцениваются при создании псевдонима типа или переменной типа. Вместо этого они оцениваются только тогда, когда это необходимо для разрешения доступа к атрибуту.
Пример:
>>> 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]
Лениво оцениваемые значения оцениваются в annotation scope, что означает, что имена, которые появляются внутри лениво оцениваемого значения, ищутся так, как если бы они использовались в непосредственно окружающей области видимости.
Added in version 3.12.
4.2.5. Встроенные модули и ограниченное выполнение¶
Детали реализации CPython: Пользователям не следует трогать __builtins__
; это исключительно деталь реализации. Пользователи, желающие переопределить значения в пространстве имен builtins, должны import
модуль builtins
и соответствующим образом изменить его атрибуты.
Пространство имен 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
выбирается в зависимости от класса экземпляра: она должна ссылаться на класс экземпляра или его non-virtual base class. Экземпляр может быть получен обработчиком и может содержать дополнительную информацию об исключительном состоянии.
Примечание
Сообщения об исключениях не являются частью API Python. Их содержимое может меняться от одной версии Python к другой без предупреждения, и на них не следует полагаться в коде, который будет работать в нескольких версиях интерпретатора.
См. также описание оператора try
в разделе Заявление try и оператора raise
в разделе Заявление raise.
Сноски