8. Составные инструкции¶
Составные инструкции содержат другие инструкции (или их группы) и тем или иным образом влияют на их выполнение или управляют ими. Как правило, составные инструкции занимают несколько строк, хотя в простых случаях вся составная инструкция может уместиться и в одной строке.
Инструкции if, while и for реализуют традиционные конструкции потока управления. try задаёт обработчики исключений и (или) код очистки для группы инструкций, а инструкция with позволяет выполнять код инициализации и завершения вокруг блока кода. Определения функций и классов также синтаксически являются составными инструкциями.
Составная инструкция состоит из одной или нескольких «частей». Часть состоит из заголовка и «набора инструкций». Заголовки частей одной составной инструкции находятся на одном уровне отступа. Каждый заголовок части начинается с однозначно определяющего её ключевого слова и заканчивается двоеточием. Набор инструкций — это группа инструкций, управляемая частью. Набор может состоять из одной или нескольких простых инструкций, разделённых точками с запятой, в той же строке, что и заголовок, после двоеточия заголовка, либо из одной или нескольких инструкций с отступом на последующих строках. Только последняя форма набора может содержать вложенные составные инструкции. Следующий пример недопустим, главным образом потому, что неясно, к какой части if относилась бы следующая за ней часть else:
if test1: if test2: print(x)
Также обратите внимание, что в этом контексте точка с запятой связывает сильнее, чем двоеточие, поэтому в следующем примере либо выполняются все вызовы print(), либо ни один:
if x < y < z: print(x); print(y); print(z)
Подводя итог:
compound_stmt:if_stmt|while_stmt|for_stmt|try_stmt|with_stmt|match_stmt|funcdef|classdef|async_with_stmt|async_for_stmt|async_funcdefsuite:stmt_listNEWLINE | NEWLINE INDENTstatement+ DEDENT statement:stmt_listNEWLINE |compound_stmtstmt_list:simple_stmt(";"simple_stmt)* [";"]
Обратите внимание, что инструкции всегда заканчиваются токеном NEWLINE, за которым, возможно, следует DEDENT. Также заметьте, что необязательные продолжающие части всегда начинаются с ключевого слова, с которого не может начинаться инструкция, поэтому неоднозначностей нет (проблема «висячего else» решается в Python требованием, чтобы вложенные инструкции if записывались с отступом).
Форматирование грамматических правил в следующих разделах помещает каждую часть на отдельную строку для наглядности.
8.1. Инструкция if¶
Инструкция if используется для условного выполнения:
if_stmt: "if"assignment_expression":"suite("elif"assignment_expression":"suite)* ["else" ":"suite]
Она выбирает ровно один из наборов инструкций, поочерёдно вычисляя выражения, пока одно из них не окажется истинным (как определяются истинные и ложные значения, см. в разделе Логические операции). После этого выполняется выбранный набор (и никакая другая часть инструкции if не выполняется и не вычисляется). Если все выражения ложны, выполняется набор инструкций из части else, если она есть.
8.2. Инструкция while¶
Инструкция while используется для повторного выполнения, пока выражение истинно:
while_stmt: "while"assignment_expression":"suite["else" ":"suite]
Она многократно проверяет выражение и, если оно истинно, выполняет первый набор инструкций. Если выражение ложно (что может случиться уже при первой проверке), выполняется набор инструкций из части else, если она есть, и цикл завершается.
Инструкция break, выполненная в первом наборе инструкций, завершает цикл без выполнения набора из части else. Инструкция continue, выполненная в первом наборе, пропускает оставшиеся в этом наборе инструкции и возвращается к проверке выражения.
8.3. Инструкция for¶
Инструкция for используется для перебора элементов последовательности (например, строки, кортежа или списка) или другого итерируемого объекта:
for_stmt: "for"target_list"in"starred_expression_list":"suite["else" ":"suite]
Выражение starred_expression_list вычисляется один раз. Оно должно давать итерируемый объект. Для этого итерируемого объекта создаётся итератор. Первый элемент, выданный итератором, затем присваивается целевому списку по стандартным правилам присваивания (см. Инструкции присваивания), и выполняется набор инструкций. Это повторяется для каждого элемента, выданного итератором. Когда итератор исчерпан, выполняется набор инструкций из части else, если она есть, и цикл завершается.
Инструкция break, выполненная в первом наборе инструкций, завершает цикл без выполнения набора из части else. Инструкция continue, выполненная в первом наборе, пропускает оставшиеся в этом наборе инструкции и переходит к следующему элементу или к части else, если следующего элемента нет.
Цикл for присваивает значения переменным из целевого списка. При этом перезаписываются все предыдущие присваивания этим переменным, включая сделанные в наборе инструкций самого цикла:
for i in range(10):
print(i)
i = 5 # это не повлияет на цикл for,
# потому что i будет перезаписана
# следующим индексом из диапазона
Имена из целевого списка не удаляются по завершении цикла, но если последовательность пуста, цикл вообще не присвоит им никаких значений. Подсказка: встроенный тип range() представляет неизменяемые арифметические последовательности целых чисел. Например, при переборе range(3) последовательно выдаются 0, 1 и затем 2.
Изменено в версии 3.11: Теперь в списке выражений разрешены элементы со звёздочкой.
8.4. Инструкция try¶
Инструкция try задаёт обработчики исключений, а также код очистки для группы инструкций:
try_stmt:try1_stmt|try2_stmt|try3_stmttry1_stmt: "try" ":"suite("except" [expression["as"identifier]] ":"suite)+ ["else" ":"suite] ["finally" ":"suite] try2_stmt: "try" ":"suite("except" "*"expression["as"identifier] ":"suite)+ ["else" ":"suite] ["finally" ":"suite] try3_stmt: "try" ":"suite"finally" ":"suite
Дополнительные сведения об исключениях см. в разделе Исключения, а сведения об использовании инструкции raise для возбуждения исключений — в разделе Инструкция raise.
Изменено в версии 3.14: Скобки вокруг нескольких типов исключений стали необязательными. См. PEP 758.
8.4.1. Часть except¶
Части except задают один или несколько обработчиков исключений. Если в части try исключение не возникло, ни один обработчик не выполняется. Когда исключение возникает в наборе инструкций try, начинается поиск обработчика исключения. Этот поиск по очереди просматривает части except, пока не найдёт ту, которая соответствует исключению. Часть except без выражения, если она есть, должна быть последней. Она соответствует любому исключению.
Для части except с выражением это выражение должно вычисляться в тип исключения или в кортеж типов исключений. Если указано несколько типов исключений и часть as не используется, скобки можно опустить. Возбуждённое исключение соответствует части except, выражение которой вычисляется в класс или в невиртуальный базовый класс объекта исключения либо в кортеж, содержащий такой класс.
Если ни одна часть except не соответствует исключению, поиск обработчика исключения продолжается в окружающем коде и в стеке вызовов. [1]
Если при вычислении выражения в заголовке части except возникает исключение, исходный поиск обработчика отменяется, и начинается поиск обработчика для нового исключения в окружающем коде и в стеке вызовов (это происходит так, как если бы исключение возбудила вся инструкция try).
Когда найдена подходящая часть except, исключение присваивается цели, указанной после ключевого слова as в этой части except (если она есть), и выполняется набор инструкций части except. Каждая часть except должна содержать исполняемый блок. По достижении конца этого блока выполнение продолжается как обычно после всей инструкции try. (Это означает, что если для одного исключения есть два вложенных обработчика и исключение возникает в части try внутреннего обработчика, внешний обработчик это исключение не обработает.)
Когда исключение присвоено с помощью as target, оно очищается в конце части except. Это происходит так, как если бы этот код:
except E as N:
foo
был преобразован в такой:
except E as N:
try:
foo
finally:
del N
Это значит, что для обращения к исключению после части except его нужно присвоить другому имени. Исключения очищаются потому, что вместе с прикреплённой к ним трассировкой они образуют цикл ссылок с кадром стека, из-за чего все локальные переменные этого кадра остаются живыми до следующей сборки мусора.
Перед выполнением набора инструкций части except исключение сохраняется в модуле sys, откуда к нему можно обратиться из тела части except, вызвав sys.exception(). При выходе из обработчика исключения сохранённое в модуле sys исключение возвращается к прежнему значению:
>>> print(sys.exception())
None
>>> try:
... raise TypeError
... except:
... print(repr(sys.exception()))
... try:
... raise ValueError
... except:
... print(repr(sys.exception()))
... print(repr(sys.exception()))
...
TypeError()
ValueError()
TypeError()
>>> print(sys.exception())
None
8.4.2. Часть except*¶
Части except* задают один или несколько обработчиков для групп исключений (экземпляров BaseExceptionGroup). Инструкция try может содержать либо части except, либо части except*, но не те и другие вместе. В случае except* тип исключения для сопоставления обязателен, поэтому except*: — синтаксическая ошибка. Тип интерпретируется так же, как в случае except, но сопоставление выполняется для исключений, содержащихся в обрабатываемой группе. Если тип для сопоставления является подклассом BaseExceptionGroup, возбуждается TypeError, так как семантика была бы неоднозначной.
Когда в блоке try возбуждается группа исключений, каждая часть except* разделяет её (см. split()) на подгруппы подходящих и неподходящих исключений. Если подгруппа подходящих исключений не пуста, она становится обрабатываемым исключением (значением, которое возвращает sys.exception()) и присваивается цели части except* (если она есть). Затем выполняется тело части except*. Если подгруппа неподходящих исключений не пуста, она таким же образом обрабатывается следующей частью except*. Так продолжается, пока все исключения в группе не будут сопоставлены или пока не отработает последняя часть except*.
После выполнения всех частей except* группа необработанных исключений объединяется со всеми исключениями, которые были возбуждены или повторно возбуждены внутри частей except*. Эта объединённая группа исключений распространяется дальше:
>>> try:
... raise ExceptionGroup("eg",
... [ValueError(1), TypeError(2), OSError(3), OSError(4)])
... except* TypeError as e:
... print(f'поймано {type(e)} с вложенными {e.exceptions}')
... except* OSError as e:
... print(f'поймано {type(e)} с вложенными {e.exceptions}')
...
поймано <class 'ExceptionGroup'> с вложенными (TypeError(2),)
поймано <class 'ExceptionGroup'> с вложенными (OSError(3), OSError(4))
+ Exception Group Traceback (most recent call last):
| File "<doctest default[0]>", line 2, in <module>
| raise ExceptionGroup("eg",
| [ValueError(1), TypeError(2), OSError(3), OSError(4)])
| ExceptionGroup: eg (1 sub-exception)
+-+---------------- 1 ----------------
| ValueError: 1
+------------------------------------
Если исключение, возбуждённое в блоке try, не является группой исключений и его тип соответствует одной из частей except*, оно перехватывается и оборачивается в группу исключений с пустой строкой сообщения. Это гарантирует, что цель e всегда имеет тип BaseExceptionGroup:
>>> try:
... raise BlockingIOError
... except* BlockingIOError as e:
... print(repr(e))
...
ExceptionGroup('', (BlockingIOError(),))
Инструкции break, continue и return не могут находиться в части except*.
8.4.3. Часть else¶
Необязательная часть else выполняется, если поток управления выходит из набора инструкций try, исключение не было возбуждено и не выполнялась ни одна из инструкций return, continue или break. Исключения в части else не обрабатываются предшествующими частями except.
8.4.4. Часть finally¶
Если указана часть finally, она задаёт обработчик «очистки». Выполняется часть try, включая все части except и else. Если в какой-либо из частей возникает исключение, которое не обрабатывается, оно временно сохраняется. Затем выполняется часть finally. Если есть сохранённое исключение, оно повторно возбуждается в конце части finally. Если часть finally возбуждает другое исключение, сохранённое исключение становится контекстом нового. Если часть finally выполняет инструкцию return, break или continue, сохранённое исключение отбрасывается. Например, эта функция возвращает 42.
def f():
try:
1/0
finally:
return 42
Во время выполнения части finally информация об исключении программе недоступна.
Когда инструкция return, break или continue выполняется в наборе инструкций try инструкции try…finally, часть finally также выполняется «на выходе».
Возвращаемое значение функции определяется последней выполненной инструкцией return. Поскольку часть finally выполняется всегда, инструкция return, выполненная в части finally, всегда будет выполнена последней. Следующая функция возвращает „finally“.
def foo():
try:
return 'try'
finally:
return 'finally'
Изменено в версии 3.8: До Python 3.8 инструкция continue была недопустима в части finally из-за проблемы в реализации.
Изменено в версии 3.14: Компилятор выдаёт предупреждение SyntaxWarning, если в блоке finally встречается return, break или continue (см. PEP 765).
8.5. Инструкция with¶
Инструкция with используется для обрамления выполнения блока методами, определёнными менеджером контекста (см. раздел Менеджеры контекста инструкции with). Это позволяет инкапсулировать типичные схемы использования try…except…finally для удобного повторного применения.
with_stmt: "with" ( "("with_stmt_contents","? ")" |with_stmt_contents) ":"suitewith_stmt_contents:with_item(","with_item)* with_item:expression["as"target]
Инструкция with с одним «элементом» выполняется следующим образом:
Вычисляется контекстное выражение (выражение, указанное в
with_item), в результате чего получается менеджер контекста.Метод
__enter__()менеджера контекста загружается для последующего использования.Метод
__exit__()менеджера контекста загружается для последующего использования.Вызывается метод
__enter__()менеджера контекста.Если в инструкцию
withвключена цель, ей присваивается значение, возвращённое методом__enter__().Примечание
Инструкция
withгарантирует, что если метод__enter__()завершается без ошибки, то__exit__()будет вызван всегда. Поэтому ошибка, возникшая при присваивании целевому списку, обрабатывается так же, как ошибка внутри набора инструкций. См. шаг 7 ниже.Выполняется набор инструкций.
Вызывается метод
__exit__()менеджера контекста. Если выход из набора инструкций был вызван исключением, его тип, значение и трассировка передаются в__exit__()в качестве аргументов. В противном случае передаются три аргументаNone.Если выход из набора инструкций произошёл из-за исключения, а метод
__exit__()вернул ложное значение, исключение возбуждается повторно. Если же возвращено истинное значение, исключение подавляется, и выполнение продолжается с инструкции, следующей за инструкциейwith.Если выход из набора инструкций произошёл по любой причине, кроме исключения, значение, возвращённое методом
__exit__(), игнорируется, и выполнение продолжается в обычном месте, соответствующем виду произошедшего выхода.
Следующий код:
with EXPRESSION as TARGET:
SUITE
семантически эквивалентен такому:
manager = (EXPRESSION)
enter = manager.__enter__
exit = manager.__exit__
value = enter()
hit_except = False
try:
TARGET = value
SUITE
except:
hit_except = True
if not exit(*sys.exc_info()):
raise
finally:
if not hit_except:
exit(None, None, None)
за исключением того, что для __enter__() и __exit__() используется неявный поиск специальных методов.
Если элементов больше одного, менеджеры контекста обрабатываются так, как если бы несколько инструкций with были вложены друг в друга:
with A() as a, B() as b:
SUITE
семантически эквивалентен такому:
with A() as a:
with B() as b:
SUITE
Менеджеры контекста с несколькими элементами можно также записывать в несколько строк, если элементы заключены в скобки. Например:
with (
A() as a,
B() as b,
):
SUITE
Изменено в версии 3.1: Добавлена поддержка нескольких контекстных выражений.
Изменено в версии 3.10: Добавлена возможность использовать скобки группировки для переноса инструкции на несколько строк.
8.6. Инструкция match¶
Добавлено в версии 3.10.
Инструкция сопоставления используется для сопоставления с шаблонами. Синтаксис:
match_stmt: 'match'subject_expr":" NEWLINE INDENTcase_block+ DEDENT subject_expr:flexible_expression"," [flexible_expression_list[',']] |assignment_expressioncase_block: 'case'patterns[guard] ":"suite
Примечание
В этом разделе одинарные кавычки обозначают мягкие ключевые слова.
При сопоставлении с шаблоном на вход принимаются шаблон (после case) и сопоставляемое значение (после match). Шаблон (который может содержать вложенные шаблоны) сопоставляется с этим значением. Результаты таковы:
Успех или неудача сопоставления (также называется успехом или неудачей шаблона).
Возможное связывание сопоставленных значений с именем. Условия для этого рассматриваются ниже.
Ключевые слова match и case являются мягкими ключевыми словами.
См. также
8.6.1. Обзор¶
Вот обзор логики работы инструкции сопоставления:
Вычисляется сопоставляемое выражение
subject_expr, и получается сопоставляемое значение. Если сопоставляемое выражение содержит запятую, строится кортеж по стандартным правилам.Шаблон каждого
case_blockпо очереди сопоставляется с вычисленным значением. Конкретные правила успеха или неудачи описаны ниже. Попытка сопоставления может также связать некоторые или все отдельные имена внутри шаблона. Точные правила связывания зависят от типа шаблона и указаны ниже. Связывания имён, сделанные при успешном сопоставлении с шаблоном, переживают выполненный блок и могут использоваться после инструкции сопоставления.Примечание
При неудачном сопоставлении некоторые вложенные шаблоны могут завершаться успешно. Не полагайтесь на то, что связывания будут выполнены при неудачном сопоставлении. И наоборот, не полагайтесь на то, что переменные останутся неизменными после неудачного сопоставления. Точное поведение зависит от реализации и может различаться. Это сознательное решение, позволяющее разным реализациям добавлять оптимизации.
Если шаблон подошёл, вычисляется соответствующее охранное условие (если оно есть). В этом случае гарантируется, что все связывания имён уже произошли.
Если охранное условие истинно или отсутствует, выполняется
blockвнутриcase_block.В противном случае пробуется следующий
case_block, как описано выше.Если других блоков case больше нет, инструкция сопоставления завершается.
Примечание
Как правило, не следует полагаться на то, что шаблон будет вычислен. В зависимости от реализации интерпретатор может кешировать значения или использовать другие оптимизации, пропускающие повторные вычисления.
Пример инструкции match:
>>> flag = False
>>> match (100, 200):
... case (100, 300): # Не совпадает: 200 != 300
... print('Случай 1')
... case (100, 200) if flag: # Совпадает, но охранное условие ложно
... print('Случай 2')
... case (100, y): # Совпадает и связывает y со значением 200
... print(Случай 3, y: {y}')
... case _: # Шаблон не проверяется
... print('Случай 4, я совпадаю с чем угодно!')
...
Case 3, y: 200
Здесь if flag — это охранное условие. Подробнее о нём — в следующем разделе.
8.6.2. Охранные условия¶
guard: "if" assignment_expression
Охранное условие (guard), входящее в case, должно быть истинным, чтобы мог выполниться код внутри блока case. Оно имеет вид: if и следующее за ним выражение.
Логика работы блока case с guard такова:
Проверить, что шаблон в блоке
caseподошёл. Если шаблон не подошёл,guardне вычисляется, и проверяется следующий блокcase.Если шаблон подошёл, вычислить
guard.Если условие
guardистинно, блок case выбирается.Если условие
guardложно, блок case не выбирается.Если
guardвозбуждает исключение при вычислении, оно всплывает наверх.
Охранные условия могут иметь побочные эффекты, поскольку они являются выражениями. Вычисление охранных условий должно идти от первого блока case к последнему, по одному, пропуская блоки case, не все шаблоны которых подошли. (То есть охранные условия должны вычисляться по порядку.) Вычисление охранных условий должно прекратиться, как только блок case выбран.
8.6.3. Неопровержимые блоки case¶
Неопровержимый блок case — это блок case, подходящий под всё. В инструкции сопоставления может быть не более одного неопровержимого блока case, и он должен быть последним.
Блок case считается неопровержимым, если у него нет охранного условия и его шаблон неопровержим. Шаблон считается неопровержимым, если только по его синтаксису можно доказать, что он всегда будет подходить. Неопровержимыми являются только следующие шаблоны:
AS-шаблоны, левая часть которых неопровержима
OR-шаблоны, содержащие хотя бы один неопровержимый шаблон
неопровержимые шаблоны в скобках
8.6.4. Шаблоны¶
Примечание
В этом разделе используются обозначения грамматики, выходящие за рамки стандартной EBNF:
обозначение
SEP.RULE+— сокращённая запись дляRULE (SEP RULE)*обозначение
!RULE— сокращённая запись для отрицательной опережающей проверки
Синтаксис верхнего уровня для patterns:
patterns:open_sequence_pattern|patternpattern:as_pattern|or_patternclosed_pattern: |literal_pattern|capture_pattern|wildcard_pattern|value_pattern|group_pattern|sequence_pattern|mapping_pattern|class_pattern
В описаниях ниже для наглядности приведено пояснение «простыми словами» того, что делает шаблон (благодарим Рэймонда Хеттингера за документ, вдохновивший большинство этих описаний). Обратите внимание, что эти пояснения служат лишь для иллюстрации и могут не отражать фактическую реализацию. Кроме того, они охватывают не все допустимые формы.
8.6.4.1. OR-шаблоны¶
OR-шаблон — это два или более шаблонов, разделённых вертикальными чертами |. Синтаксис:
or_pattern: "|".closed_pattern+
Только последний вложенный шаблон может быть неопровержимым, и каждый вложенный шаблон должен связывать один и тот же набор имён, чтобы избежать неоднозначности.
OR-шаблон по очереди сопоставляет каждый из своих вложенных шаблонов с сопоставляемым значением, пока один не подойдёт. После этого OR-шаблон считается успешным. В противном случае, если ни один из вложенных шаблонов не подошёл, OR-шаблон терпит неудачу.
Простыми словами, P1 | P2 | ... пробует сопоставить P1, при неудаче пробует P2 и далее по списку. Как только какой-то из шаблонов подходит, сопоставление успешно завершается, а если ни один не подошёл — терпит неудачу.
8.6.4.2. AS-шаблоны¶
AS-шаблон сопоставляет OR-шаблон, стоящий слева от ключевого слова as, с сопоставляемым значением. Синтаксис:
as_pattern:or_pattern"as"capture_pattern
Если OR-шаблон не подошёл, AS-шаблон тоже не подходит. В противном случае AS-шаблон связывает сопоставляемое значение с именем справа от ключевого слова as и завершается успешно. capture_pattern не может быть _.
Простыми словами, P as NAME сопоставляется с P и в случае успеха выполняет NAME = <subject>.
8.6.4.3. Литеральные шаблоны¶
Литеральный шаблон соответствует большинству литералов в Python. Синтаксис:
literal_pattern:signed_number|signed_number"+" NUMBER |signed_number"-" NUMBER |strings| "None" | "True" | "False" signed_number: ["+" | "-"] NUMBER
Правило strings и токен NUMBER определены в стандартной грамматике Python. Строки в тройных кавычках поддерживаются. Сырые строки и байтовые строки поддерживаются. f-строки и t-строки не поддерживаются.
Формы signed_number '+' NUMBER и signed_number '-' NUMBER служат для записи комплексных чисел. Они требуют вещественное число слева и мнимое справа. Например, 3 + 4j.
Простыми словами, LITERAL подойдёт, только если <subject> == LITERAL. Для синглтонов None, True и False используется оператор is.
8.6.4.4. Шаблоны захвата¶
Шаблон захвата связывает сопоставляемое значение с именем. Синтаксис:
capture_pattern: !'_' NAME
Одиночное подчёркивание _ не является шаблоном захвата (это выражено записью !'_'). Вместо этого оно рассматривается как wildcard_pattern.
В данном шаблоне одно и то же имя может быть связано только один раз. Например, case x, x: ... недопустимо, а case [x] | x: ... разрешено.
Шаблоны захвата всегда подходят. Связывание подчиняется правилам области видимости, установленным для оператора выражения присваивания в PEP 572. Имя становится локальной переменной в ближайшей объемлющей области видимости функции, если только нет применимой инструкции global или nonlocal.
Простыми словами, NAME всегда подойдёт и выполнит NAME = <subject>.
8.6.4.5. Шаблоны-подстановки¶
Шаблон-подстановка всегда подходит (соответствует чему угодно) и не связывает никаких имён. Синтаксис:
wildcard_pattern: '_'
_ является мягким ключевым словом внутри любого шаблона, но только внутри шаблонов. Как обычно, это идентификатор, в том числе в сопоставляемых выражениях match, охранных условиях (guard) и блоках case.
Простыми словами, _ всегда подойдёт.
8.6.4.6. Шаблоны-значения¶
Шаблон-значение представляет именованное значение в Python. Синтаксис:
value_pattern:attrattr:name_or_attr"." NAME name_or_attr:attr| NAME
Имя с точками в шаблоне ищется по стандартным правилам разрешения имён Python. Шаблон подходит, если найденное значение равно сопоставляемому (при сравнении оператором равенства ==).
Простыми словами, NAME1.NAME2 подойдёт, только если <subject> == NAME1.NAME2
Примечание
Если одно и то же значение встречается несколько раз в одной инструкции сопоставления, интерпретатор может кешировать первое найденное значение и использовать его повторно, вместо того чтобы повторять поиск. Этот кеш строго привязан к конкретному выполнению конкретной инструкции сопоставления.
8.6.4.7. Групповые шаблоны¶
Групповой шаблон позволяет заключать шаблоны в скобки, чтобы подчеркнуть задуманную группировку. В остальном у него нет дополнительного синтаксиса. Синтаксис:
group_pattern: "(" pattern ")"
Простыми словами, (P) действует так же, как P.
8.6.4.8. Шаблоны последовательностей¶
Шаблон последовательности содержит несколько вложенных шаблонов, которые сопоставляются с элементами последовательности. Синтаксис похож на распаковку списка или кортежа.
sequence_pattern: "[" [maybe_sequence_pattern] "]" | "(" [open_sequence_pattern] ")" open_sequence_pattern:maybe_star_pattern"," [maybe_sequence_pattern] maybe_sequence_pattern: ",".maybe_star_pattern+ ","? maybe_star_pattern:star_pattern|patternstar_pattern: "*" (capture_pattern|wildcard_pattern)
Для шаблонов последовательностей нет разницы, используются круглые или квадратные скобки (т. е. (...) или [...]).
Примечание
Одиночный шаблон в круглых скобках без завершающей запятой (например, (3 | 4)) является групповым шаблоном, в то время как одиночный шаблон в квадратных скобках (например, [3 | 4]) является шаблоном последовательности.
В шаблоне последовательности может быть не более одного вложенного шаблона со звёздочкой. Он может находиться в любой позиции. Если вложенного шаблона со звёздочкой нет, это шаблон последовательности фиксированной длины, иначе — переменной длины.
Ниже описана логика сопоставления шаблона последовательности с сопоставляемым значением:
Если сопоставляемое значение не является последовательностью [2], шаблон последовательности не подходит.
Если сопоставляемое значение является экземпляром
str,bytesилиbytearray, шаблон последовательности не подходит.Последующие шаги зависят от того, имеет ли шаблон последовательности фиксированную или переменную длину.
Если шаблон последовательности имеет фиксированную длину:
Если длина сопоставляемой последовательности не равна числу вложенных шаблонов, шаблон последовательности не подходит
Вложенные шаблоны сопоставляются с соответствующими элементами последовательности слева направо. Сопоставление прекращается, как только какой-либо вложенный шаблон не подходит. Если все вложенные шаблоны успешно сопоставились со своими элементами, шаблон последовательности подходит.
В противном случае, если шаблон последовательности имеет переменную длину:
Если длина сопоставляемой последовательности меньше числа вложенных шаблонов без звёздочки, шаблон последовательности не подходит.
Начальные вложенные шаблоны без звёздочки сопоставляются с соответствующими элементами так же, как для последовательностей фиксированной длины.
Если предыдущий шаг успешен, вложенный шаблон со звёздочкой сопоставляется со списком из оставшихся элементов последовательности, кроме тех, что соответствуют вложенным шаблонам без звёздочки, стоящим после него.
Оставшиеся вложенные шаблоны без звёздочки сопоставляются с соответствующими элементами сопоставляемой последовательности так же, как для последовательности фиксированной длины.
Примечание
Длина сопоставляемой последовательности получается с помощью
len()(т. е. через протокол__len__()). Интерпретатор может кешировать эту длину так же, как для шаблонов-значений.
Простыми словами, [P1, P2, P3, … , P<N>] подходит, только если выполняется всё следующее:
проверка, что
<subject>— последовательностьlen(subject) == <N>P1соответствует<subject>[0](обратите внимание, что при этом сопоставлении могут также связываться имена)P2соответствует<subject>[1](обратите внимание, что при этом сопоставлении могут также связываться имена)… и так далее для соответствующих шаблонов и элементов.
8.6.4.9. Шаблоны отображений¶
Шаблон отображения содержит один или несколько шаблонов «ключ — значение». Синтаксис напоминает создание словаря. Синтаксис:
mapping_pattern: "{" [items_pattern] "}" items_pattern: ",".key_value_pattern+ ","? key_value_pattern: (literal_pattern|value_pattern) ":"pattern|double_star_patterndouble_star_pattern: "**"capture_pattern
В шаблоне отображения может быть не более одного шаблона с двумя звёздочками. Он должен быть последним вложенным шаблоном.
Повторяющиеся ключи в шаблонах отображений запрещены. Повторяющиеся литеральные ключи возбудят SyntaxError. Два ключа, различающиеся записью, но имеющие одно и то же значение, возбудят ValueError во время выполнения.
Ниже описана логика сопоставления шаблона отображения с сопоставляемым значением:
Если сопоставляемое значение не является отображением [3], шаблон отображения не подходит.
Если каждый ключ из шаблона отображения присутствует в сопоставляемом отображении и шаблон для каждого ключа подходит к соответствующему элементу этого отображения, шаблон отображения подходит.
Если в шаблоне отображения обнаружены повторяющиеся ключи, шаблон считается недопустимым. Для повторяющихся литеральных значений возбуждается
SyntaxError, а для именованных ключей с одинаковым значением —ValueError.
Примечание
Пары «ключ — значение» сопоставляются с помощью двухаргументной формы метода get() сопоставляемого отображения. Сопоставляемые пары «ключ — значение» должны уже присутствовать в отображении, а не создаваться на лету через __missing__() или __getitem__().
Простыми словами, {KEY1: P1, KEY2: P2, ... } подходит, только если выполняется всё следующее:
проверка, что
<subject>— отображениеKEY1 in <subject>P1соответствует<subject>[KEY1]… и так далее для соответствующих пар «ключ — шаблон».
8.6.4.10. Шаблоны классов¶
Шаблон класса представляет класс и его позиционные и именованные аргументы (если они есть). Синтаксис:
class_pattern:name_or_attr"(" [pattern_arguments","?] ")" pattern_arguments:positional_patterns[","keyword_patterns] |keyword_patternspositional_patterns: ",".pattern+ keyword_patterns: ",".keyword_pattern+ keyword_pattern: NAME "="pattern
Одно и то же имя не должно повторяться в шаблонах классов.
Ниже описана логика сопоставления шаблона класса с сопоставляемым значением:
Если
name_or_attrне является экземпляром встроенного типаtype, возбуждаетсяTypeError.Если сопоставляемое значение не является экземпляром
name_or_attr(проверка выполняется черезisinstance()), шаблон класса не подходит.Если аргументов шаблона нет, шаблон подходит. В противном случае последующие шаги зависят от того, есть ли именованные или позиционные шаблоны аргументов.
Для ряда встроенных типов (указаны ниже) допускается один позиционный вложенный шаблон, который сопоставляется со всем сопоставляемым значением. Для этих типов именованные шаблоны тоже работают, как и для других типов.
Если присутствуют только именованные шаблоны, они обрабатываются по одному следующим образом:
Имя ищется как атрибут сопоставляемого значения.
Если при этом возбуждается исключение, отличное от
AttributeError, оно всплывает наверх.Если при этом возбуждается
AttributeError, шаблон класса не подошёл.В остальных случаях вложенный шаблон, связанный с именованным шаблоном, сопоставляется со значением атрибута. Если сопоставление не удалось, шаблон класса не подходит. Если удалось, сопоставление переходит к следующему имени.
Если все именованные шаблоны подошли, шаблон класса подходит.
Если присутствуют позиционные шаблоны, перед сопоставлением они преобразуются в именованные с помощью атрибута
__match_args__классаname_or_attr:Вызывается эквивалент
getattr(cls, "__match_args__", ()).Если при этом возбуждается исключение, оно всплывает наверх.
Если возвращённое значение не является кортежем, преобразование не удаётся и возбуждается
TypeError.Если позиционных шаблонов больше, чем
len(cls.__match_args__), возбуждаетсяTypeError.В остальных случаях позиционный шаблон
iпреобразуется в именованный, причём в качестве имени используется__match_args__[i].__match_args__[i]должно быть строкой, иначе возбуждаетсяTypeError.Если имена повторяются, возбуждается
TypeError.
После того как все позиционные шаблоны преобразованы в именованные, сопоставление продолжается так, как если бы были только именованные шаблоны.
Для следующих встроенных типов обработка позиционных вложенных шаблонов отличается:
Эти классы принимают единственный позиционный аргумент, и стоящий там шаблон сопоставляется со всем объектом, а не с атрибутом. Например,
int(0|1)соответствует значению0, но не значению0.0.
Простыми словами, CLS(P1, attr=P2) подходит, только если выполняется следующее:
isinstance(<subject>, CLS)преобразование
P1в именованный шаблон с помощьюCLS.__match_args__Для каждого именованного аргумента
attr=P2:hasattr(<subject>, "attr")P2соответствует<subject>.attr
… и так далее для соответствующих пар «именованный аргумент — шаблон».
8.7. Определения функций¶
Определение функции задаёт пользовательский объект функции (см. раздел Иерархия стандартных типов):
funcdef: [decorators] "def"funcname[type_params] "(" [parameter_list] ")" ["->"expression] ":"suitedecorators:decorator+ decorator: "@"assignment_expressionNEWLINE parameter_list:defparameter(","defparameter)* "," "/" ["," [parameter_list_no_posonly]] |parameter_list_no_posonlyparameter_list_no_posonly:defparameter(","defparameter)* ["," [parameter_list_starargs]] |parameter_list_starargsparameter_list_starargs: "*"star_parameter(","defparameter)* ["," [parameter_star_kwargs]] | "*" (","defparameter)+ ["," [parameter_star_kwargs]] |parameter_star_kwargsparameter_star_kwargs: "**"parameter[","] parameter:identifier[":"expression] star_parameter:identifier[":" ["*"]expression] defparameter:parameter["="expression] funcname:identifier
Определение функции — исполняемая инструкция. Её выполнение связывает имя функции в текущем локальном пространстве имён с объектом функции (обёрткой вокруг исполняемого кода функции). Этот объект функции содержит ссылку на текущее глобальное пространство имён, которое будет использоваться как глобальное при вызове функции.
Определение функции не выполняет тело функции. Оно выполняется только при вызове функции. [4]
Определение функции может быть обёрнуто одним или несколькими выражениями декораторов. Выражения декораторов вычисляются при определении функции в области видимости, содержащей это определение. Результат должен быть вызываемым объектом, который вызывается с объектом функции в качестве единственного аргумента. Возвращённое значение связывается с именем функции вместо самого объекта функции. Несколько декораторов применяются вложенно. Например, следующий код
@f1(arg)
@f2
def func(): pass
примерно эквивалентен этому
def func(): pass
func = f1(arg)(f2(func))
за исключением того, что исходная функция не связывается временно с именем func.
Изменено в версии 3.9: Теперь функции можно декорировать любым допустимым выражением assignment_expression. Раньше грамматика была намного строже. Подробности см. в PEP 614.
Список параметров типа может быть указан в квадратных скобках между именем функции и открывающей скобкой её списка параметров. Это сообщает статическим средствам проверки типов, что функция является обобщённой. Во время выполнения параметры типа можно получить из атрибута __type_params__ функции. Подробнее см. в разделе Обобщённые функции.
Изменено в версии 3.12: Списки параметров типа появились в Python 3.12.
Если один или несколько параметров записаны в виде параметр = выражение, говорят, что функция имеет «значения параметров по умолчанию». Для параметра со значением по умолчанию соответствующий аргумент можно опустить при вызове, и тогда подставляется значение параметра по умолчанию. Если у параметра есть значение по умолчанию, то все следующие за ним параметры до «*» тоже должны иметь значение по умолчанию — это синтаксическое ограничение, которое не выражено в грамматике.
Значения параметров по умолчанию вычисляются слева направо при выполнении определения функции. Это означает, что выражение вычисляется один раз, при определении функции, и для каждого вызова используется одно и то же «заранее вычисленное» значение. Это особенно важно понимать, когда значение параметра по умолчанию — изменяемый объект, например список или словарь: если функция изменяет этот объект (например, добавляет элемент в список), значение параметра по умолчанию фактически изменяется. Как правило, это не то, что предполагалось. Обойти это можно так: использовать в качестве значения по умолчанию None и явно проверять его в теле функции, например:
def whats_on_the_telly(penguin=None):
if penguin is None:
penguin = []
penguin.append("property of the zoo")
return penguin
Семантика вызова функции подробнее описана в разделе Вызовы. Вызов функции всегда присваивает значения всем параметрам, указанным в списке параметров: из позиционных аргументов, из именованных аргументов или из значений по умолчанию. Если присутствует форма «*identifier», она инициализируется кортежем, получающим все лишние позиционные параметры, по умолчанию — пустым кортежем. Если присутствует форма «**identifier», она инициализируется новым упорядоченным отображением, получающим все лишние именованные аргументы, по умолчанию — новым пустым отображением того же типа. Параметры после «*» или «*identifier» являются только именованными и могут передаваться только как именованные аргументы. Параметры перед «/» являются только позиционными и могут передаваться только как позиционные аргументы.
Изменено в версии 3.8: Теперь для обозначения только позиционных параметров можно использовать синтаксис / в параметрах функции. См. PEP 570.
Параметры могут иметь аннотацию вида «: expression» после имени параметра. Аннотацию может иметь любой параметр, в том числе вида *identifier или **identifier. (В качестве особого случая параметры вида *identifier могут иметь аннотацию «: *expression».) После списка параметров функции может быть указана аннотация «возвращаемого значения» вида «-> expression». Эти аннотации могут быть любым допустимым выражением Python. Наличие аннотаций не меняет семантику функции. Дополнительные сведения об аннотациях см. в разделе Аннотации.
Изменено в версии 3.11: Теперь параметры вида «*identifier» могут иметь аннотацию «: *expression». См. PEP 646.
Можно также создавать анонимные функции (функции, не связанные с именем) для немедленного использования в выражениях. Для этого применяются лямбда-выражения, описанные в разделе Лямбды. Обратите внимание, что лямбда-выражение — это лишь сокращённая запись упрощённого определения функции. Функцию, определённую в инструкции «def», можно передавать или присваивать другому имени точно так же, как функцию, определённую лямбда-выражением. Форма «def» на самом деле мощнее, так как позволяет выполнять несколько инструкций и использовать аннотации.
Примечание для программистов: Функции — объекты первого класса. Инструкция «def», выполненная внутри определения функции, определяет локальную функцию, которую можно вернуть или передать дальше. Свободные переменные, используемые во вложенной функции, могут обращаться к локальным переменным функции, содержащей этот def. Подробности см. в разделе Именование и связывание.
См. также
- PEP 3107 — Аннотации функций
Исходная спецификация аннотаций функций.
- PEP 484 — Подсказки типов
Определение стандартного значения аннотаций: подсказок типов.
- PEP 526 — Синтаксис аннотаций переменных
Возможность добавлять подсказки типов к объявлениям переменных, включая переменные класса и переменные экземпляра.
- PEP 563 — Отложенное вычисление аннотаций
Поддержка опережающих ссылок внутри аннотаций за счёт сохранения аннотаций во время выполнения в строковом виде вместо немедленного вычисления.
- PEP 318 — Декораторы для функций и методов
Появились декораторы функций и методов. Декораторы классов появились в PEP 3129.
8.8. Определения классов¶
Определение класса задаёт объект класса (см. раздел Иерархия стандартных типов):
classdef: [decorators] "class"classname[type_params] [inheritance] ":"suiteinheritance: "(" [argument_list] ")" classname:identifier
Определение класса — исполняемая инструкция. Список наследования обычно задаёт список базовых классов (более сложные случаи использования см. в разделе Метаклассы), поэтому каждый элемент списка должен вычисляться в объект класса, допускающий создание подклассов. Классы без списка наследования по умолчанию наследуются от базового класса object. Следовательно,
class Foo:
pass
эквивалентено этому
class Foo(object):
pass
Базовых классов может быть один или несколько. Подробнее см. ниже в разделе Множественное наследование.
Затем набор инструкций класса выполняется в новом кадре выполнения (см. Именование и связывание) с использованием вновь созданного локального пространства имён и исходного глобального пространства имён. (Обычно набор инструкций содержит в основном определения функций.) Когда выполнение набора инструкций класса завершается, его кадр выполнения отбрасывается, но локальное пространство имён сохраняется. [5] Затем создаётся объект класса, при этом для базовых классов используется список наследования, а для словаря атрибутов — сохранённое локальное пространство имён. Имя класса связывается с этим объектом класса в исходном локальном пространстве имён.
Порядок, в котором атрибуты определены в теле класса, сохраняется в __dict__ нового класса. Учтите, что на это можно полагаться только сразу после создания класса и только для классов, определённых с помощью синтаксиса определения.
Создание класса можно сильно настроить с помощью метаклассов.
Классы тоже можно декорировать: так же, как при декорировании функций,
@f1(arg)
@f2
class Foo: pass
примерно эквивалентен этому
class Foo: pass
Foo = f1(arg)(f2(Foo))
Правила вычисления выражений декораторов такие же, как для декораторов функций. Затем результат связывается с именем класса.
Изменено в версии 3.9: Теперь классы можно декорировать любым допустимым выражением assignment_expression. Раньше грамматика была намного строже. Подробности см. в PEP 614.
Список параметров типа может быть указан в квадратных скобках сразу после имени класса. Это сообщает статическим средствам проверки типов, что класс является обобщённым. Во время выполнения параметры типа можно получить из атрибута __type_params__ класса. Подробнее см. в разделе Обобщённые классы.
Изменено в версии 3.12: Списки параметров типа появились в Python 3.12.
Примечание для программистов: Переменные, определённые в теле класса, являются атрибутами класса. Они общие для всех экземпляров. Атрибуты экземпляра можно задавать в методе с помощью self.name = value. И атрибуты класса, и атрибуты экземпляра доступны через запись «self.name», причём атрибут экземпляра скрывает атрибут класса с тем же именем при обращении таким способом. Атрибуты класса можно использовать как значения по умолчанию для атрибутов экземпляра, но использование там изменяемых значений может привести к неожиданным результатам. С помощью дескрипторов можно создавать переменные экземпляра с другими деталями реализации.
См. также
8.8.1. Множественное наследование¶
У классов Python может быть несколько базовых классов — приём, известный как множественное наследование. Базовые классы указываются в определении класса перечислением в скобках после имени класса через запятую. Например, следующее определение класса:
>>> class A: pass
>>> class B: pass
>>> class C(A, B): pass
определяет класс C, который наследуется от классов A и B.
Порядок разрешения методов (MRO) — это порядок, в котором просматриваются базовые классы при поиске атрибута класса. Описание того, как Python определяет MRO класса, см. в The Python 2.3 Method Resolution Order.
Множественное наследование допускается не всегда. Попытка определить класс с множественным наследованием приведёт к ошибке, если один из базовых классов не допускает создание подклассов, если нельзя построить согласованный MRO, если нельзя определить допустимый метакласс или если есть конфликт раскладок экземпляров. Каждый из этих случаев рассматривается по очереди.
Во-первых, все базовые классы должны допускать создание подклассов. Хотя большинство классов это допускают, некоторые встроенные классы, например bool, не допускают:
>>> class SubBool(bool): # TypeError
... pass
Traceback (most recent call last):
...
TypeError: type 'bool' is not an acceptable base type
В определённом MRO класса базовые классы идут в том порядке, в котором они указаны в списке базовых классов. Кроме того, в MRO дочерний класс всегда стоит перед любым из своих базовых классов. Определение класса завершится ошибкой, если по заданному списку базовых классов невозможно построить согласованный MRO, удовлетворяющий этим правилам:
>>> class Base: pass
>>> class Child(Base): pass
>>> class Grandchild(Base, Child): pass # TypeError
Traceback (most recent call last):
...
TypeError: Cannot create a consistent method resolution order (MRO) for bases Base, Child
В MRO Grandchild класс Base должен стоять перед Child, потому что он первый в списке базовых классов, но он же должен стоять после Child, потому что является его родителем. Это противоречие, поэтому класс определить нельзя.
Если у некоторых базовых классов есть пользовательский метакласс, метакласс получаемого класса выбирается среди метаклассов базовых классов и явно указанного метакласса дочернего класса. Это должен быть метакласс, являющийся подклассом всех остальных метаклассов-кандидатов. Если среди кандидатов такого метакласса нет, класс создать нельзя, как объясняется в разделе Определение подходящего метакласса.
Наконец, раскладки экземпляров базовых классов должны быть совместимы. Это означает, что для класса должно быть возможно вычислить опорный базовый класс. Какие именно классы являются опорными базовыми, зависит от реализации Python.
В CPython класс является опорным базовым, если у него есть непустое определение __slots__. Многие, но не все классы, определённые на C, тоже являются опорными базовыми, включая большинство встроенных (таких как int или BaseException), но исключая большинство конкретных классов Exception. Как правило, класс на C является опорным базовым, если размер лежащей в его основе структуры отличается от размера структуры его базового класса.
У каждого класса есть опорный базовый класс. У базового класса object опорным базовым классом является он сам. Если базовый класс один и он опорный, опорным для дочернего класса становится он сам, а если он не опорный, то опорным становится его опорный базовый класс. Если базовых классов несколько, сначала для каждого из них определяется опорный базовый класс, в результате чего получается список кандидатов. Если среди кандидатов есть единственный опорный базовый класс, являющийся подклассом всех остальных, то он и есть опорный базовый класс. В противном случае создание класса завершается ошибкой.
Пример:
>>> class Solid1:
... __slots__ = ("solid1",)
>>>
>>> class Solid2:
... __slots__ = ("solid2",)
>>>
>>> class SolidChild(Solid1):
... __slots__ = ("solid_child",)
>>>
>>> class C1: # опорный базовый класс — `object`
... pass
>>>
>>> # OK: опорные базовые классы — `Solid1` и `object`, и `Solid1` является подклассом `object`.
>>> class C2(Solid1, C1): # опорный базовый класс — `Solid1`
... pass
>>>
>>> # OK: опорные базовые классы — `SolidChild` и `Solid1`, и `SolidChild` является подклассом `Solid1`.
>>> class C3(SolidChild, Solid1): # опорный базовый класс — `SolidChild`
... pass
>>>
>>> # Ошибка: опорные базовые классы — `Solid1` и `Solid2`, но ни один из них не является подклассом другого.
>>> class C4(Solid1, Solid2): # ошибка: нет единого опорного базового класса
... pass
Traceback (most recent call last):
...
TypeError: multiple bases have instance lay-out conflict
8.9. Сопрограммы¶
Добавлено в версии 3.5.
8.9.1. Определение сопрограммной функции¶
async_funcdef: [decorators] "async" "def"funcname"(" [parameter_list] ")" ["->"expression] ":"suite
Выполнение сопрограмм Python можно приостанавливать и возобновлять во многих точках (см. сопрограмма). Выражения await, async for и async with можно использовать только в теле сопрограммной функции.
Функции, определённые с синтаксисом async def, всегда являются сопрограммными функциями, даже если не содержат ключевых слов await или async.
Использование выражения yield from внутри тела сопрограммной функции является ошибкой SyntaxError.
Пример сопрограммной функции:
async def func(param1, param2):
do_stuff()
await some_coroutine()
Изменено в версии 3.7: await и async теперь являются ключевыми словами. Раньше они считались таковыми только внутри тела сопрограммной функции.
8.9.2. Инструкция async for¶
async_for_stmt: "async" for_stmt
Асинхронный итерируемый объект предоставляет метод __aiter__, который напрямую возвращает асинхронный итератор, способный вызывать асинхронный код в своём методе __anext__.
Инструкция async for позволяет удобно перебирать асинхронные итерируемые объекты.
Следующий код:
async for TARGET in ITER:
SUITE
else:
SUITE2
Семантически эквивалентно этому:
iter = (ITER).__aiter__()
running = True
while running:
try:
TARGET = await iter.__anext__()
except StopAsyncIteration:
running = False
else:
SUITE
else:
SUITE2
за исключением того, что для __aiter__() и __anext__() используется неявный поиск специальных методов.
Использование инструкции async for вне тела сопрограммной функции является ошибкой SyntaxError.
8.9.3. Инструкция async with¶
async_with_stmt: "async" with_stmt
Асинхронный менеджер контекста — это менеджер контекста, способный приостанавливать выполнение в своих методах входа и выхода.
Следующий код:
async with EXPRESSION as TARGET:
SUITE
семантически эквивалентен такому:
manager = (EXPRESSION)
aenter = manager.__aenter__
aexit = manager.__aexit__
value = await aenter()
hit_except = False
try:
TARGET = value
SUITE
except:
hit_except = True
if not await aexit(*sys.exc_info()):
raise
finally:
if not hit_except:
await aexit(None, None, None)
за исключением того, что для __aenter__() и __aexit__() используется неявный поиск специальных методов.
Использование инструкции async with вне тела сопрограммной функции является ошибкой SyntaxError.
См. также
- PEP 492 — Сопрограммы с синтаксисом async и await
Предложение, сделавшее сопрограммы полноценной самостоятельной концепцией в Python и добавившее синтаксис для работы с ними.
8.10. Списки параметров типа¶
Добавлено в версии 3.12.
Изменено в версии 3.13: Теперь поддерживаются значения по умолчанию (см. PEP 696).
type_params: "["type_param(","type_param)* "]" type_param:typevar|typevartuple|paramspectypevar:identifier(":"expression)? ("="expression)? typevartuple: "*"identifier("="expression)? paramspec: "**"identifier("="expression)?
Функции (включая сопрограммы), классы и псевдонимы типов могут содержать список параметров типа:
def max[T](args: list[T]) -> T:
...
async def amax[T](args: list[T]) -> T:
...
class Bag[T]:
def __iter__(self) -> Iterator[T]:
...
def add(self, arg: T) -> None:
...
type ListOrSet[T] = list[T] | set[T]
С точки зрения семантики это означает, что функция, класс или псевдоним типа являются обобщёнными по переменной типа. Эта информация в основном используется статическими средствами проверки типов, а во время выполнения обобщённые объекты ведут себя во многом так же, как их необобщённые аналоги.
Параметры типа объявляются в квадратных скобках ([]) сразу после имени функции, класса или псевдонима типа. Параметры типа доступны в области видимости обобщённого объекта, но не за её пределами. Поэтому после объявления def func[T](): pass имя T недоступно в области видимости модуля. Ниже семантика обобщённых объектов описана более точно. Область видимости параметров типа моделируется специальной функцией (технически — областью видимости аннотаций), которая оборачивает создание обобщённого объекта.
У обобщённых функций, классов и псевдонимов типов есть атрибут __type_params__, перечисляющий их параметры типа.
Параметры типа бывают трёх видов:
typing.TypeVar, задаваемый простым именем (например,T). С точки зрения семантики это представляет для средства проверки типов один тип.typing.TypeVarTuple, задаваемый именем с одной звёздочкой в начале (например,*Ts). С точки зрения семантики это обозначает кортеж из любого числа типов.typing.ParamSpec, задаваемый именем с двумя звёздочками в начале (например,**P). С точки зрения семантики это обозначает параметры вызываемого объекта.
Объявления typing.TypeVar могут задавать границы и ограничения с помощью двоеточия (:), за которым следует выражение. Одно выражение после двоеточия задаёт верхнюю границу (например, T: int). С точки зрения семантики это означает, что typing.TypeVar может представлять только подтипы этой границы. Заключённый в скобки кортеж выражений после двоеточия задаёт набор ограничений (например, T: (str, bytes)). Каждый элемент кортежа должен быть типом (опять же, во время выполнения это не проверяется). Переменные типа с ограничениями могут принимать только один из типов, перечисленных в наборе ограничений.
Для объектов typing.TypeVar, объявленных с помощью синтаксиса списка параметров типа, граница и ограничения не вычисляются при создании обобщённого объекта, а только при явном обращении к значению через атрибуты __bound__ и __constraints__. Для этого границы или ограничения вычисляются в отдельной области видимости аннотаций.
У объектов typing.TypeVarTuple и typing.ParamSpec не может быть границ или ограничений.
Все три разновидности параметров типа могут также иметь значение по умолчанию, которое используется, если параметр типа не указан явно. Оно добавляется знаком равенства (=), за которым следует выражение. Подобно границам и ограничениям переменных типа, значение по умолчанию не вычисляется при создании объекта, а только при обращении к атрибуту __default__ параметра типа. Для этого значение по умолчанию вычисляется в отдельной области видимости аннотаций. Если для параметра типа значение по умолчанию не указано, атрибуту __default__ присваивается специальный сторожевой объект typing.NoDefault.
Следующий пример показывает полный набор допустимых объявлений параметров типа:
def overly_generic[
SimpleTypeVar,
TypeVarWithBound: int,
TypeVarWithConstraints: (str, bytes),
TypeVarWithDefault = int,
*SimpleTypeVarTuple = (int, float),
**SimpleParamSpec = (str, bytearray),
](
a: SimpleTypeVar,
b: TypeVarWithDefault,
c: TypeVarWithBound,
d: Callable[SimpleParamSpec, TypeVarWithConstraints],
*e: SimpleTypeVarTuple,
): ...
8.10.1. Обобщённые функции¶
Обобщённые функции объявляются следующим образом:
def func[T](arg: T): ...
Этот синтаксис эквивалентен следующему:
annotation-def TYPE_PARAMS_OF_func():
T = typing.TypeVar("T")
def func(arg: T): ...
func.__type_params__ = (T,)
return func
func = TYPE_PARAMS_OF_func()
Здесь annotation-def обозначает область видимости аннотаций, которая во время выполнения на самом деле не связана ни с каким именем. (При этом преобразовании допущена ещё одна вольность: синтаксис не обращается к атрибуту модуля typing, а создаёт экземпляр typing.TypeVar напрямую.)
Аннотации обобщённых функций вычисляются в области видимости аннотаций, используемой для объявления параметров типа, а значения по умолчанию и декораторы функции — нет.
Следующий пример иллюстрирует правила областей видимости для этих случаев, а также для других разновидностей параметров типа:
@decorator
def func[T: int, *Ts, **P](*args: *Ts, arg: Callable[P, T] = some_default):
...
За исключением ленивого вычисления границы TypeVar, это эквивалентно следующему:
DEFAULT_OF_arg = some_default
annotation-def TYPE_PARAMS_OF_func():
annotation-def BOUND_OF_T():
return int
# In reality, BOUND_OF_T() is evaluated only on demand.
T = typing.TypeVar("T", bound=BOUND_OF_T())
Ts = typing.TypeVarTuple("Ts")
P = typing.ParamSpec("P")
def func(*args: *Ts, arg: Callable[P, T] = DEFAULT_OF_arg):
...
func.__type_params__ = (T, Ts, P)
return func
func = decorator(TYPE_PARAMS_OF_func())
Имена, написанные заглавными буквами, например DEFAULT_OF_arg, во время выполнения на самом деле ни с чем не связываются.
8.10.2. Обобщённые классы¶
Обобщённые классы объявляются следующим образом:
class Bag[T]: ...
Этот синтаксис эквивалентен следующему:
annotation-def TYPE_PARAMS_OF_Bag():
T = typing.TypeVar("T")
class Bag(typing.Generic[T]):
__type_params__ = (T,)
...
return Bag
Bag = TYPE_PARAMS_OF_Bag()
Здесь снова annotation-def (не настоящее ключевое слово) обозначает область видимости аннотаций, а имя TYPE_PARAMS_OF_Bag во время выполнения на самом деле ни с чем не связывается.
Обобщённые классы неявно наследуются от typing.Generic. Базовые классы и именованные аргументы обобщённых классов вычисляются в области видимости параметров типа, а декораторы — вне этой области. Это иллюстрирует следующий пример:
@decorator
class Bag[T](Base[T], arg=T): ...
Это эквивалентно следующему:
annotation-def TYPE_PARAMS_OF_Bag():
T = typing.TypeVar("T")
class Bag(Base[T], typing.Generic[T], arg=T):
__type_params__ = (T,)
...
return Bag
Bag = decorator(TYPE_PARAMS_OF_Bag())
8.10.3. Обобщённые псевдонимы типов¶
Инструкцию type можно также использовать для создания обобщённого псевдонима типа:
type ListOrSet[T] = list[T] | set[T]
За исключением ленивого вычисления значения, это эквивалентно следующему:
annotation-def TYPE_PARAMS_OF_ListOrSet():
T = typing.TypeVar("T")
annotation-def VALUE_OF_ListOrSet():
return list[T] | set[T]
# На самом деле значение вычисляется лениво
return typing.TypeAliasType("ListOrSet", VALUE_OF_ListOrSet(), type_params=(T,))
ListOrSet = TYPE_PARAMS_OF_ListOrSet()
Здесь annotation-def (не настоящее ключевое слово) обозначает область видимости аннотаций. Имена, написанные заглавными буквами, например TYPE_PARAMS_OF_ListOrSet, во время выполнения на самом деле ни с чем не связываются.
8.11. Аннотации¶
Изменено в версии 3.14: Теперь аннотации по умолчанию вычисляются лениво.
Переменные и параметры функций могут иметь аннотации, которые создаются добавлением двоеточия после имени, за которым следует выражение:
x: annotation = 1
def f(param: annotation): ...
Функции также могут иметь аннотацию возвращаемого значения после стрелки:
def f() -> annotation: ...
Обычно аннотации используются для подсказок типов, но язык этого не требует, и в общем случае аннотации могут содержать произвольные выражения. Наличие аннотаций не меняет семантику кода во время выполнения, если только не используется механизм, который выполняет интроспекцию аннотаций и использует их (например, dataclasses или @functools.singledispatch).
По умолчанию аннотации вычисляются лениво в области видимости аннотаций. Это означает, что они не вычисляются при выполнении кода, содержащего аннотацию. Вместо этого интерпретатор сохраняет информацию, с помощью которой аннотацию можно вычислить позже по запросу. Модуль annotationlib предоставляет средства для вычисления аннотаций.
Если присутствует инструкция будущих возможностей from __future__ import annotations, все аннотации вместо этого сохраняются в виде строк:
>>> from __future__ import annotations
>>> def f(param: annotation): ...
>>> f.__annotations__
{'param': 'annotation'}
Эта инструкция будущих возможностей будет объявлена устаревшей и удалена в одной из будущих версий Python, но не раньше, чем Python 3.13 достигнет конца жизненного цикла (см. PEP 749). Когда она используется, средства интроспекции, такие как annotationlib.get_annotations() и typing.get_type_hints(), с меньшей вероятностью смогут разрешить аннотации во время выполнения.
Примечания