Составные операторы
Составные операторы содержат (группы) других операторов; они каким-то образом влияют на выполнение этих других операторов или управляют им. В общем случае составные операторы занимают несколько строк, хотя в простых вариантах весь составной оператор может поместиться в одной строке.
Операторы 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_funcdef
suite ::= stmt_list NEWLINE | NEWLINE INDENT statement+ DEDENT
statement ::= stmt_list NEWLINE | compound_stmt
stmt_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_list ":" suite
["else" ":" suite]
Выражение starred_list вычисляется один раз; оно должно возвращать итерируемый объект. Для этого итерируемого объекта создается итератор. Первый элемент, предоставленный итератором, затем присваивается списку целей с использованием стандартных правил присваивания (см. Операторы присваивания), и выполняется тело. Это повторяется для каждого элемента, предоставляемого итератором. Когда итератор исчерпан, выполняется тело блока else, если он присутствует, и цикл завершается.
Оператор break, выполняемый в первом теле, завершает цикл, не выполняя тело блока else. Оператор continue, выполняемый в первом теле, пропускает остальную часть тела и переходит к следующему элементу или к блоку else, если следующего элемента нет.
Цикл for выполняет присваивания переменным в списке целей. Это перезаписывает все предыдущие присваивания этим переменным, включая те, что были сделаны в теле цикла for:
for i in range(10):
print(i)
i = 5 # this will not affect the for-loop
# because i will be overwritten with the next
# index in the range
Имена в списке целей не удаляются после завершения цикла, но если последовательность пустая, к ним не будет выполнено никакого присваивания циклом. Подсказка: встроенный тип range() представляет неизменяемые арифметические последовательности целых чисел. Например, итерация range(3) последовательно возвращает 0, 1 и затем 2.
Изменено в версии 3.11: Теперь разрешены звёздчатые элементы в списке выражений.
8.4. Оператор try
Оператор try задает обработчики исключений и/или код очистки для группы операторов:
try_stmt ::= try1_stmt | try2_stmt | try3_stmt
try1_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.
8.4.1. Блок except
Блок(и) except задают один или несколько обработчиков исключений. Если в блоке try не происходит исключение, никакой обработчик исключений не выполняется. Если в блоке try возникает исключение, начинается поиск обработчика исключений. Этот поиск последовательно проверяет блоки except, пока не найдётся такой, который соответствует данному исключению. Если присутствует блок except без выражения, он должен быть последним; он соответствует любому исключению.
Для блока except с выражением выражение должно быть типом исключения или кортежем типов исключений. Возникшее исключение соответствует блоку 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* используются для обработки ExceptionGroupов. Тип исключения для сопоставления интерпретируется так же, как и в случае блока except, но в случае групп исключений мы можем иметь частичные совпадения, когда тип совпадает с некоторыми исключениями в группе. Это означает, что несколько блоков except* могут быть выполнены, каждый из которых обрабатывает часть группы исключений. Каждый блок выполняется не более одного раза и обрабатывает группу исключений, включающую все соответствующие исключения. Каждое исключение в группе обрабатывается не более одним блоком except*, первым, который ему соответствует.
>>> try:
... raise ExceptionGroup("eg",
... [ValueError(1), TypeError(2), OSError(3), OSError(4)])
... except* TypeError as e:
... print(f'caught {type(e)} with nested {e.exceptions}')
... except* OSError as e:
... print(f'caught {type(e)} with nested {e.exceptions}')
...
caught <class 'ExceptionGroup'> with nested (TypeError(2),)
caught <class 'ExceptionGroup'> with nested (OSError(3), OSError(4))
+ Exception Group Traceback (most recent call last):
| File "<stdin>", line 2, in <module>
| ExceptionGroup: eg
+-+---------------- 1 ----------------
| ValueError: 1
+------------------------------------
Любые оставшиеся исключения, которые не были обработаны ни одним блоком except*, повторно генерируются в конце, вместе со всеми исключениями, которые были генерированы внутри блоков except*. Если этот список содержит более одного исключения для повторного генерирования, они объединяются в группу исключений.
Если генерируемое исключение не является группой исключений, и его тип соответствует одному из блоков except*, оно перехватывается и оборачивается в группу исключений со строкой сообщения, равной пустой строке.
>>> try:
... raise BlockingIOError
... except* BlockingIOError as e:
... print(repr(e))
...
ExceptionGroup('', (BlockingIOError()))
Блок except* должен иметь соответствующее выражение; он не может быть except*:. Кроме того, это выражение не может содержать типы групп исключений, так как это привело бы к неоднозначному семантическому значению.
Смешивание блоков except и блоков except* в одном блоке try недопустимо. 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, сохранённое исключение отбрасывается:
>>> def f(): ... try: ... 1/0 ... finally: ... return 42 ... >>> f() 42
Информация об исключении недоступна программе во время выполнения блока finally.
Когда оператор return, break или continue выполняется в блоке try оператора try…finally, блок finally также выполняется ‘по пути выхода’.
Значение возврата функции определяется последним оператором return, который был выполнен. Поскольку блок finally всегда выполняется, оператор return, выполненный в блоке finally, всегда будет последним выполненным:
>>> def foo(): ... try: ... return 'try' ... finally: ... return 'finally' ... >>> foo() 'finally'
Изменено в версии 3.8: До Python 3.8, оператор continue был запрещён в блоке finally из-за проблемы с реализацией.
8.5. Оператор with
Оператор with используется для обертывания выполнения блока методами, определёнными менеджером контекста (см. раздел Менеджеры контекста оператора With). Это позволяет обобщить распространённые шаблоны try…except…finally для удобного повторного использования.
with_stmt ::= "with" ( "(" with_stmt_contents ","? ")" | with_stmt_contents ) ":" suite
with_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 = type(manager).__enter__
exit = type(manager).__exit__
value = enter(manager)
hit_except = False
try:
TARGET = value
SUITE
except:
hit_except = True
if not exit(manager, *sys.exc_info()):
raise
finally:
if not hit_except:
exit(manager, None, None, None)
При использовании более одного элемента, менеджеры контекста обрабатываются как если бы были вложены несколько операторов 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 используется для сопоставления с образцом. Синтаксис:
match_stmt ::= 'match' subject_expr ":" NEWLINE INDENT case_block+ DEDENT subject_expr ::=star_named_expression","star_named_expressions? |named_expressioncase_block ::= 'case' patterns [guard] ":"block
Примечание
В этом разделе для обозначения мягких ключевых слов используются одинарные кавычки.
Сопоставление с образцом принимает шаблон в качестве входных данных (после case) и значение объекта (после match). Шаблон (который может содержать подшаблоны) сопоставляется со значением объекта. Возможные результаты:
- Успех или неудача сопоставления (также называемый успехом или неудачей шаблона).
- Возможная привязка сопоставленных значений к имени. Предварительные условия для этого обсуждаются ниже.
Ключевые слова match и case являются мягкими ключевыми словами.
См. также
8.6.1. Обзор
Вот обзор логического потока оператора match:
- Выражение объекта
subject_exprвычисляется, и получается значение объекта. Если выражение объекта содержит запятую, создается кортеж с использованием стандартных правил. -
Каждый шаблон в блоке
case_blockпытается сопоставиться со значением объекта. Условия успеха или неудачи описаны ниже. Попытка сопоставления может также привязать некоторые или все независимые имена в шаблоне. Точные правила привязки шаблонов различаются в зависимости от типа шаблона и указаны ниже. Связывания имен, сделанные во время успешного сопоставления шаблона, сохраняются после выполнения блока и могут быть использованы после оператора match.Примечание
Во время неудачных сопоставлений шаблонов некоторые подшаблоны могут быть успешны. Не полагайтесь на то, что привязки будут сделаны для неудачного сопоставления. Аналогично, не полагайтесь на то, что переменные останутся неизменными после неудачного сопоставления. Точное поведение зависит от реализации и может отличаться. Это умышленное решение, которое позволяет различным реализациям добавлять оптимизации.
-
Если шаблон совпадает, оценивается соответствующий ограничитель (если он есть). В этом случае все привязки имен гарантированно произошли.
- Если ограничитель оценивается как истина или отсутствует, выполняется
blockвнутриcase_block. - В противном случае следующая попытка сопоставления
case_blockвыполняется, как описано выше. - Если других блоков case нет, оператор match завершается.
- Если ограничитель оценивается как истина или отсутствует, выполняется
Примечание
Пользователи, как правило, никогда не должны полагаться на то, что шаблон будет вычисляться. В зависимости от реализации интерпретатор может кэшировать значения или использовать другие оптимизации, которые пропускают повторные вычисления.
Пример оператора match:
>>> flag = False
>>> match (100, 200):
... case (100, 300): # Mismatch: 200 != 300
... print('Case 1')
... case (100, 200) if flag: # Successful match, but guard fails
... print('Case 2')
... case (100, y): # Matches and binds y to 200
... print(f'Case 3, y: {y}')
... case _: # Pattern not attempted
... print('Case 4, I match anything!')
...
Case 3, y: 200
В этом случае if flag — это ограничитель. Подробнее об этом в следующем разделе.
8.6.2. Ограничители
guard ::= "if" named_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, который соответствует всем случаям. В операторе match может быть не более одного неопровержимого блока case, и он должен быть последним.
Блок case считается неопровержимым, если у него нет ограничителя и его шаблон является неопровержимым. Шаблон считается неопровержимым, если по его синтаксису можно доказать, что он всегда будет успешным. Только следующие шаблоны являются неопровержимыми:
- Шаблоны AS, левая часть которых является неопровержимой
- Шаблоны OR содержащие, по крайней мере, один неопровержимый шаблон
- Шаблоны захвата
- Шаблоны-заменители
- скобочные неопровержимые шаблоны
8.6.4. Шаблоны
Примечание
В этом разделе используются обозначения грамматики, выходящие за рамки стандартного EBNF:
- обозначение
SEP.RULE+— сокращение дляRULE (SEP RULE)* - обозначение
!RULE— сокращение для утверждения отрицательного предпросмотра
Синтаксис верхнего уровня для patterns:
patterns ::= open_sequence_pattern | pattern
pattern ::= as_pattern | or_pattern
closed_pattern ::= | literal_pattern
| capture_pattern
| wildcard_pattern
| value_pattern
| group_pattern
| sequence_pattern
| mapping_pattern
| class_pattern
В описаниях ниже будет включено описание «простыми словами» того, что делает шаблон, для целей иллюстрации (благодаря Раймонду Хеттингера за документ, который вдохновил на большинство описаний). Обратите внимание, что эти описания предназначены только для целей иллюстрации и могут не отражать реализацию. Кроме того, они не охватывают все допустимые формы.
8.6.4.1. Шаблоны ИЛИ
Шаблон ИЛИ — это два или более шаблонов, разделенных вертикальными чертами |. Синтаксис:
or_pattern ::= "|".closed_pattern+
Только конечный подшаблон может быть неотказуемым, и каждый подшаблон должен связывать один и тот же набор имён, чтобы избежать неоднозначности.
Шаблон ИЛИ последовательно сопоставляет каждый из своих подшаблонов со значением субъекта, пока один из них не окажется успешным. Затем шаблон ИЛИ считается успешным. В противном случае, если ни один из подшаблонов не увенчается успехом, шаблон ИЛИ терпит неудачу.
Простыми словами, P1 | P2 | ... будет пытаться сопоставить P1, если это не удастся, он будет пытаться сопоставить P2, сразу же добившись успеха, если любой из них окажется успешным, и потерпев неудачу в противном случае.
8.6.4.2. Шаблоны AS
Шаблон AS сопоставляет шаблон ИЛИ слева от ключевого слова as со значением субъекта. Синтаксис:
as_pattern ::= or_pattern "as" capture_pattern
Если шаблон ИЛИ терпит неудачу, то и шаблон 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 | "-" NUMBER
Правило strings и токен NUMBER определены в стандартной грамматике Python. Поддерживаются тройные кавычки. Поддерживаются сырые строки и строки байтов. f-строки не поддерживаются.
Формы 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 ::= attr attr ::= 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 | pattern
star_pattern ::= "*" (capture_pattern | wildcard_pattern)
Разницы нет, если для шаблонов последовательностей используются круглые или квадратные скобки (т.е. (...) vs [...]).
Примечание
Один шаблон в круглых скобках без заключительной запятой (например, (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_pattern
double_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_patterns
positional_patterns ::= ",".pattern+
keyword_patterns ::= ",".keyword_pattern+
keyword_pattern ::= NAME "=" pattern
Один и тот же именованный аргумент не должен повторяться в образцах классов.
Логическая последовательность сопоставления шаблона класса со значением объекта:
- Если
name_or_attrне является экземпляром встроенногоtype, вызовитеTypeError. - Если значение объекта не является экземпляром
name_or_attr(проверка с помощьюisinstance()), шаблон класса не соответствует. -
Если аргументов шаблона нет, шаблон соответствует. В противном случае последующие шаги зависят от наличия именованных или позиционных аргументов шаблона.
Для ряда встроенных типов (указанных ниже) принимается один позиционный подшаблон, который будет соответствовать всему объекту; для этих типов именованные шаблоны также работают так же, как и для других типов.
Если присутствуют только именованные шаблоны, они обрабатываются по одному:
I. Именованный аргумент ищется как атрибут объекта.
- Если это вызывает исключение, отличное от
AttributeError, исключение передаётся дальше. - Если это вызывает
AttributeError, шаблон класса не соответствует. - В противном случае подшаблон, связанный с именованным шаблоном, сопоставляется со значением атрибута объекта. Если это не соответствует, шаблон класса не соответствует; если соответствует, соответствие переходит к следующему именованному аргументу.
II. Если все именованные шаблоны соответствуют, шаблон класса соответствует.
Если присутствуют позиционные шаблоны, они преобразуются в именованные шаблоны с использованием атрибута
__match_args__классаname_or_attrперед сопоставлением:I. Вызывается эквивалент
getattr(cls, "__match_args__", ()).- Если это вызывает исключение, исключение передаётся дальше.
- Если возвращаемое значение не является кортежем, преобразование завершается неудачно, и поднимается
TypeError. - Если позиционных шаблонов больше, чем
len(cls.__match_args__), поднимаетсяTypeError. - В противном случае позиционный шаблон
iпреобразуется в именованный шаблон с использованием__match_args__[i]в качестве именованного аргумента.__match_args__[i]должен быть строкой; в противном случае поднимаетсяTypeError. - Если есть повторяющиеся именованные аргументы, поднимается
TypeError.
- Если это вызывает исключение, отличное от
8.7. Определения функций
Определение функции определяет объект пользовательской функции (см. раздел Иерархия стандартных типов):
funcdef ::= [decorators] "def" funcname [type_params] "(" [parameter_list] ")"
["->" expression] ":" suite
decorators ::= decorator+
decorator ::= "@" assignment_expression NEWLINE
parameter_list ::= defparameter ("," defparameter)* "," "/" ["," [parameter_list_no_posonly]]
| parameter_list_no_posonly
parameter_list_no_posonly ::= defparameter ("," defparameter)* ["," [parameter_list_starargs]]
| parameter_list_starargs
parameter_list_starargs ::= "*" [parameter] ("," defparameter)* ["," ["**" parameter [","]]]
| "**" parameter [","]
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. Функции могут иметь аннотацию «возвращаемого значения» вида «-> expression» после списка параметров. Эти аннотации могут быть любым допустимым выражением Python. Наличие аннотаций не изменяет семантики функции. Значения аннотаций доступны как значения словаря, ключами которого являются имена параметров, в атрибуте __annotations__ объекта функции. Если используется импорт annotations из __future__, аннотации сохраняются как строки во время выполнения, что позволяет отложить вычисление. В противном случае они вычисляются при выполнении определения функции. В этом случае аннотации могут быть вычислены в порядке, отличном от того, в котором они появляются в исходном коде.
Также можно создать анонимные функции (функции, не привязанные к имени), для непосредственного использования в выражениях. Это используется с выражениями lambda, описанными в разделе Lambda-выражения. Обратите внимание, что выражение lambda — это всего лишь сокращённая запись для упрощённого определения функции; функция, определённая в операторе «def», может передаваться или присваиваться другому имени, как функция, определённая с помощью выражения lambda. Форма «def» фактически более мощная, поскольку она позволяет выполнить несколько операторов и аннотаций.
Примечание для программиста: Функции являются объектами первого класса. Оператор «def», выполненный внутри определения функции, определяет вложенную функцию, которую можно возвращать или передавать. Свободные переменные, используемые во вложенной функции, могут получить доступ к локальным переменным функции, содержащей def. См. раздел Имена и привязка для получения подробной информации.
См. также
- PEP 3107 - Аннотации функций
-
Исходное описание аннотаций функций.
- PEP 484 - Указатели типов
-
Определение стандартного значения для аннотаций: указатели типов.
- PEP 526 - Синтаксис аннотаций переменных
-
Возможность указания типов для объявлений переменных, включая переменные класса и переменные экземпляра.
- PEP 563 - Отложенное вычисление аннотаций
-
Поддержка ссылок вперёд в аннотациях путём сохранения аннотаций в виде строк во время выполнения вместо немедленного вычисления.
- PEP 318 - Декораторы для функций и методов
-
Были введены декораторы функций и методов. Классовые декораторы были введены в PEP 3129.
8.8. Определения классов
Определение класса определяет объект класса (см. раздел Иерархия стандартных типов):
classdef ::= [decorators] "class" classname [type_params] [inheritance] ":" suite
inheritance ::= "(" [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.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)
iter = type(iter).__aiter__(iter)
running = True
while running:
try:
TARGET = await type(iter).__anext__(iter)
except StopAsyncIteration:
running = False
else:
SUITE
else:
SUITE2
См. также __aiter__() и __anext__() для получения подробной информации.
Использование инструкции async for вне тела функции корутины является SyntaxError.
8.9.3. Инструкция async with
async_with_stmt ::= "async" with_stmt
Асинхронный менеджер контекста — это менеджер контекста, который способен приостанавливать выполнение в своих методах enter и exit.
Следующий код:
async with EXPRESSION as TARGET:
SUITE
семантически эквивалентен:
manager = (EXPRESSION)
aenter = type(manager).__aenter__
aexit = type(manager).__aexit__
value = await aenter(manager)
hit_except = False
try:
TARGET = value
SUITE
except:
hit_except = True
if not await aexit(manager, *sys.exc_info()):
raise
finally:
if not hit_except:
await aexit(manager, None, None, None)
См. также __aenter__() и __aexit__() для получения подробной информации.
Использование инструкции async with вне тела функции корутины является SyntaxError.
См. также
- PEP 492 - Корутины с синтаксисом async и await
-
Предложение, сделавшее корутины полноценной самостоятельной концепцией в Python, и добавившее поддерживающий синтаксис.
8.10. Списки параметров типа
Добавлен в версии 3.12.
type_params ::= "[" type_param ("," type_param)* "]"
type_param ::= typevar | typevartuple | paramspec
typevar ::= identifier (":" expression)?
typevartuple ::= "*" identifier
paramspec ::= "**" identifier
Функции (включая кортежи), классы и алиасы типов могут содержать список параметров типа:
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 не могут иметь границ или ограничений.
Следующий пример демонстрирует полный набор разрешённых объявлений параметров типа:
def overly_generic[ SimpleTypeVar, TypeVarWithBound: int, TypeVarWithConstraints: (str, bytes), *SimpleTypeVarTuple, **SimpleParamSpec, ]( a: SimpleTypeVar, b: TypeVarWithBound, c: Callable[SimpleParamSpec, TypeVarWithConstraints], *d: 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(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]
# In reality, the value is lazily evaluated
return typing.TypeAliasType("ListOrSet", VALUE_OF_ListOrSet(), type_params=(T,))
ListOrSet = TYPE_PARAMS_OF_ListOrSet()
Здесь annotation-def (не является реальным ключевым словом) обозначает область аннотации. Заглавные имена, такие как TYPE_PARAMS_OF_ListOrSet, фактически не привязаны во время выполнения.
Примечания
© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/reference/compound_stmts.html