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_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]

The starred_list expression is evaluated once; it should yield an iterable object. An iterator is created for that iterable. The first item provided by the iterator is then assigned to the target list using the standard rules for assignments (see Заяви про призначення), and the suite is executed. This repeats for each item provided by the iterator. When the iterator is exhausted, the suite in the else clause, if present, is executed, and the loop terminates.

Інструкція 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

Names in the target list are not deleted when the loop is finished, but if the sequence is empty, they will not have been assigned to at all by the loop. Hint: the built-in type range() represents immutable arithmetic sequences of integers. For instance, iterating range(3) successively yields 0, 1, and then 2.

Змінено в версії 3.11: Starred elements are now allowed in the expression list.

8.4. Оператор try

The try statement specifies exception handlers and/or cleanup code for a group of statements:

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 clause

The except clause(s) specify one or more exception handlers. When no exception occurs in the try clause, no exception handler is executed. When an exception occurs in the try suite, a search for an exception handler is started. This search inspects the except clauses in turn until one is found that matches the exception. An expression-less except clause, if present, must be last; it matches any exception. For an except clause with an expression, that expression is evaluated, and the clause matches the exception if the resulting object is «compatible» with the exception. An object is compatible with an exception if the object is the class or a non-virtual base class of the exception object, or a tuple containing an item that is the class or a non-virtual base class of the exception object.

If no except clause matches the exception, the search for an exception handler continues in the surrounding code and on the invocation stack. [1]

If the evaluation of an expression in the header of an except clause raises an exception, the original search for a handler is canceled and a search starts for the new exception in the surrounding code and on the call stack (it is treated as if the entire try statement raised the exception).

When a matching except clause is found, the exception is assigned to the target specified after the as keyword in that except clause, if present, and the except clause’s suite is executed. All except clauses must have an executable block. When the end of this block is reached, execution continues normally after the entire try statement. (This means that if two nested handlers exist for the same exception, and the exception occurs in the try clause of the inner handler, the outer handler will not handle the exception.)

When an exception has been assigned using as target, it is cleared at the end of the except clause. This is as if

except E as N:
    foo

було перекладено на:

except E as N:
    try:
        foo
    finally:
        del N

This means the exception must be assigned to a different name to be able to refer to it after the except clause. Exceptions are cleared because with the traceback attached to them, they form a reference cycle with the stack frame, keeping all locals in that frame alive until the next garbage collection occurs.

Before an except clause’s suite is executed, the exception is stored in the sys module, where it can be accessed from within the body of the except clause by calling sys.exception(). When leaving an exception handler, the exception stored in the sys module is reset to its previous value:

>>> 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* clause

The except* clause(s) are used for handling ExceptionGroups. The exception type for matching is interpreted as in the case of except, but in the case of exception groups we can have partial matches when the type matches some of the exceptions in the group. This means that multiple except* clauses can execute, each handling part of the exception group. Each clause executes at most once and handles an exception group of all matching exceptions. Each exception in the group is handled by at most one except* clause, the first that matches it.

>>> 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
    +------------------------------------

Any remaining exceptions that were not handled by any except* clause are re-raised at the end, along with all exceptions that were raised from within the except* clauses. If this list contains more than one exception to reraise, they are combined into an exception group.

If the raised exception is not an exception group and its type matches one of the except* clauses, it is caught and wrapped by an exception group with an empty message string.

>>> try:
...     raise BlockingIOError
... except* BlockingIOError as e:
...     print(repr(e))
...
ExceptionGroup('', (BlockingIOError()))

An except* clause must have a matching type, and this type cannot be a subclass of BaseExceptionGroup. It is not possible to mix except and except* in the same try. break, continue and return cannot appear in an except* clause.

8.4.3. else clause

Необов’язкова пропозиція else виконується, якщо потік керування виходить із набору try, не було викликано винятків і немає return, continue або Інструкція break була виконана. Винятки в пункті else не обробляються попередніми пунктами except.

8.4.4. finally clause

If finally is present, it specifies a „cleanup“ handler. The try clause is executed, including any except and else clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The finally clause is executed. If there is a saved exception it is re-raised at the end of the finally clause. If the finally clause raises another exception, the saved exception is set as the context of the new exception. If the finally clause executes a return, break or continue statement, the saved exception is discarded:

>>> def f():
...     try:
...         1/0
...     finally:
...         return 42
...
>>> f()
42

The exception information is not available to the program during execution of the finally clause.

When a return, break or continue statement is executed in the try suite of a tryfinally statement, the finally clause is also executed „on the way out.“

The return value of a function is determined by the last return statement executed. Since the finally clause always executes, a return statement executed in the finally clause will always be the last one executed:

>>> def foo():
...     try:
...         return 'try'
...     finally:
...         return 'finally'
...
>>> foo()
'finally'

Змінено в версії 3.8: Prior to Python 3.8, a continue statement was illegal in the finally clause due to a problem with the implementation.

8.5. Оператор with

Оператор with використовується для обгортання виконання блоку методами, визначеними контекстним менеджером (див. розділ З менеджерами контексту операторів). Це дозволяє інкапсулювати загальні tryexceptfinally моделі використання для зручного повторного використання.

with_stmt          ::=  "with" ( "(" with_stmt_contents ","? ")" | with_stmt_contents ) ":" suite
with_stmt_contents ::=  with_item ("," with_item)*
with_item          ::=  expression ["as" target]

Виконання оператора with з одним «елементом» відбувається наступним чином:

  1. Контекстний вираз (вираз, поданий у with_item) обчислюється для отримання контекстного менеджера.

  2. The context manager’s __enter__() is loaded for later use.

  3. The context manager’s __exit__() is loaded for later use.

  4. The context manager’s __enter__() method is invoked.

  5. If a target was included in the with statement, the return value from __enter__() is assigned to it.

    Примітка

    The with statement guarantees that if the __enter__() method returns without an error, then __exit__() will always be called. Thus, if an error occurs during the assignment to the target list, it will be treated the same as an error occurring within the suite would be. See step 7 below.

  6. Сюїта виконана.

  7. The context manager’s __exit__() method is invoked. If an exception caused the suite to be exited, its type, value, and traceback are passed as arguments to __exit__(). Otherwise, three None arguments are supplied.

    If the suite was exited due to an exception, and the return value from the __exit__() method was false, the exception is reraised. If the return value was true, the exception is suppressed, and execution continues with the statement following the with statement.

    If the suite was exited for any reason other than an exception, the return value from __exit__() is ignored, and execution proceeds at the normal location for the kind of exit that was taken.

Наступний код:

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: Підтримка використання групування дужок для розбиття оператора на кілька рядків.

Дивись також

PEP 343 - оператор «з».

Специфікація, передумови та приклади оператора Python with.

8.6. Оператор match

Added in version 3.10.

Оператор match використовується для зіставлення шаблону. Синтаксис:

match_stmt   ::=  'match' subject_expr ":" NEWLINE INDENT case_block+ DEDENT
subject_expr ::=  star_named_expression "," star_named_expressions?
                  | named_expression
case_block   ::=  'case' patterns [guard] ":" block

Примітка

У цьому розділі використовуються одинарні лапки для позначення м’яких ключових слів.

Зіставлення шаблону приймає шаблон як вхідні дані (після case) і значення теми (після match). Шаблон (який може містити підшаблони) зіставляється зі значенням теми. Результати:

  • Успіх або невдача збігу (також називається успішним або невдалим шаблоном).

  • Можливе прив’язування відповідних значень до імені. Передумови для цього обговорюються нижче.

Ключові слова match і case є м’якими ключовими словами.

Дивись також

  • PEP 634 – Зіставлення структурних шаблонів: Специфікація

  • PEP 636 – Зіставлення структурних шаблонів: підручник

8.6.1. Огляд

Ось огляд логічної послідовності оператора збігу:

  1. Вираз суб’єкта subject_expr обчислюється та отримується результуюче значення предмета. Якщо вираз теми містить кому, кортеж створюється за допомогою стандартних правил.

  2. Кожен шаблон у case_block намагається зіставити зі значенням теми. Конкретні правила успіху чи невдачі описані нижче. Спроба збігу також може пов’язати деякі або всі окремі імена в шаблоні. Точні правила зв’язування шаблону залежать від типу шаблону та вказані нижче. Прив’язки імен, зроблені під час успішного збігу шаблону, переживають виконаний блок і можуть використовуватися після оператора збігу.

    Примітка

    Під час невдалих збігів шаблонів деякі підшаблони можуть бути успішними. Не покладайтеся на прив’язки, зроблені для невдалого матчу. І навпаки, не покладайтеся на те, що змінні залишаться незмінними після невдалого збігу. Точна поведінка залежить від реалізації та може відрізнятися. Це навмисне рішення, яке дозволяє оптимізувати різні реалізації.

  3. Якщо шаблон успішний, відповідний охоронець (якщо присутній) оцінюється. У цьому випадку всі прив’язки імен гарантовано відбулися.

    • Якщо guard оцінює як true або відсутній, виконується block всередині case_block.

    • Інакше виконується спроба наступного case_block, як описано вище.

    • Якщо немає інших блоків регістру, оператор збігу завершується.

Примітка

Зазвичай користувачі ніколи не повинні покладатися на шаблон, що оцінюється. Залежно від реалізації, інтерпретатор може кешувати значення або використовувати інші оптимізації, які пропускають повторні оцінки.

Зразок заяви про відповідність:

>>> 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 наступна:

  1. Переконайтеся, що шаблон у блоці case виконано успішно. Якщо шаблон не вдається, guard не оцінюється, і перевіряється наступний case блок.

  2. Якщо шаблон вдався, оцініть guard.

    • Якщо умова guard оцінюється як істинна, вибирається блок регістру.

    • Якщо умова guard оцінюється як false, блок регістру не вибрано.

    • Якщо охоронець викликає виняток під час оцінки, виняток з’являється.

Охоронцям дозволено мати побічні ефекти, оскільки вони є виразами. Оцінка Guard повинна проходити від першого до останнього блоку регістрів, по одному, пропускаючи блоки регістрів, шаблон(и) яких не всі є успішними. (Тобто оцінка охорони має відбуватися в порядку.) Оцінка охорони має припинитися, коли вибрано блок справи.

8.6.3. Незаперечні блоки випадків

Незаперечний блок регістрів — це блок регістрів, що відповідає всім регістрам. Інструкція збігу може мати щонайбільше один неспростовний блок регістру, і він має бути останнім.

Блок футляра вважається незаперечним, якщо він не має захисного елемента і його візерунок є неспростовним. Патерн вважається неспростовним, якщо ми можемо довести лише з його синтаксису, що він завжди матиме успіх. Незаперечними є лише такі закономірності:

8.6.4. Візерунки

Примітка

У цьому розділі використовуються граматичні нотації поза стандартними EBNF:

  • позначення SEP.RULE+ є скороченням для RULE (SEP RULE)*

  • нотація !RULE є скороченням для негативного твердження попереднього перегляду

Синтаксис верхнього рівня для патернів такий:

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+

Лише остаточний підшаблон може бути irrefutable, і кожен підшаблон повинен пов’язувати однаковий набір імен, щоб уникнути двозначності.

Шаблон АБО зіставляє кожен зі своїх підшаблонів по черзі зі значенням суб’єкта, поки один не досягне успіху. Потім шаблон АБО вважається успішним. В іншому випадку, якщо жоден із підшаблонів не вдасться, шаблон АБО буде невдалим.

Простіше кажучи, P1 | P2 | ... намагатиметься знайти відповідність P1, якщо це не вдасться, вона спробує знайти P2, успішно негайно, якщо будь-який з них вдасться, інакше не вдасться.

8.6.4.2. AS Patterns

Шаблон AS відповідає шаблону АБО ліворуч від ключового слова as щодо теми. Синтаксис:

as_pattern ::=  or_pattern "as" capture_pattern

If the OR pattern fails, the AS pattern fails. Otherwise, the AS pattern binds the subject to the name on the right of the as keyword and succeeds. capture_pattern cannot be a _.

Простіше кажучи, 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

Правило рядки і токен ЧИСЛО визначено в стандартній граматиці Python. Підтримуються рядки в потрійних лапках. Підтримуються необроблені рядки та рядки байтів. f-strings не підтримуються.

Форми число_знак '+' ЧИСЛО і число_знак '-' ЧИСЛО призначені для вираження комплексних чисел; вони вимагають дійсного числа зліва та уявного числа справа. наприклад 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, guards і 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)

Немає різниці, якщо дужки або квадратні дужки використовуються для шаблонів послідовності (тобто (...) проти [...] ).

Примітка

Один шаблон, укладений у дужки без кінцевої коми (наприклад, (3 | 4)), є груповим шаблоном. У той час як один шаблон, укладений у квадратні дужки (наприклад, [3 | 4]), все ще є шаблоном послідовності.

Щонайбільше один зірковий підшаблон може бути в шаблоні послідовності. Підшаблон зірки може з’являтися в будь-якій позиції. Якщо підшаблон зірки відсутній, шаблон послідовності є шаблоном послідовності фіксованої довжини; інакше це шаблон послідовності змінної довжини.

Нижче наведено логічний потік для зіставлення шаблону послідовності зі значенням предмета:

  1. Якщо значення теми не є послідовністю [2], шаблон послідовності не виконується.

  2. Якщо значення теми є екземпляром str, bytes або bytearray, шаблон послідовності не виконується.

  3. Подальші кроки залежать від того, чи є шаблон послідовності фіксованою чи змінною довжиною.

    Якщо шаблон послідовності має фіксовану довжину:

    1. Якщо довжина предметної послідовності не дорівнює кількості підшаблонів, шаблон послідовності не вдається

    2. Підшаблони в шаблоні послідовності зіставляються з відповідними елементами в послідовності предметів зліва направо. Зіставлення припиняється, як тільки підшаблон не вдається. Якщо всі підшаблони успішно відповідають їхньому відповідному елементу, шаблон послідовності вдається.

    В іншому випадку, якщо шаблон послідовності має змінну довжину:

    1. Якщо довжина предметної послідовності менша за кількість незіркових підшаблонів, шаблон послідовності не вдається.

    2. Провідні незіркові підшаблони зіставляються з відповідними елементами, як для послідовностей фіксованої довжини.

    3. Якщо попередній крок виконано успішно, підшаблон зірочки збігається зі списком, сформованим із решти предметних елементів, за винятком решти елементів, що відповідають підшаблонам без зірочки, які слідують за підшаблоном зірочки.

    4. Решта підшаблонів без зірок зіставляються з відповідними предметними елементами, як для послідовності фіксованої довжини.

    Примітка

    Довжина предметної послідовності визначається через 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 під час виконання.

Нижче наведено логічний потік для зіставлення шаблону зіставлення зі значенням предмета:

  1. Якщо значення предмета не є відображенням [3], шаблон відображення не виконується.

  2. Якщо кожен ключ, поданий у шаблоні відображення, присутній у відображенні предмета, і шаблон для кожного ключа збігається з відповідним елементом відображення предмета, шаблон відображення є успішним.

  3. Якщо в шаблоні відображення виявлено повторювані ключі, шаблон вважається недійсним. SyntaxError виникає для повторюваних літеральних значень; або ValueError для іменованих ключів з тим самим значенням.

Примітка

Key-value pairs are matched using the two-argument form of the mapping subject’s get() method. Matched key-value pairs must already be present in the mapping, and not created on-the-fly via __missing__() or __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

Те саме ключове слово не повинно повторюватися в шаблонах класів.

Нижче наведено логічний потік для зіставлення шаблону класу зі значенням предмета:

  1. Якщо name_or_attr не є екземпляром вбудованого type , викликати TypeError.

  2. Якщо значення теми не є екземпляром name_or_attr (перевірено через isinstance()), шаблон класу не вдається.

  3. Якщо аргументи шаблону відсутні, шаблон виконується успішно. В іншому випадку наступні кроки залежать від того, чи присутні шаблони ключових слів або позиційних аргументів.

    Для ряду вбудованих типів (зазначених нижче) приймається один позиційний підшаблон, який відповідатиме всьому об’єкту; для цих типів шаблони ключових слів також працюють, як і для інших типів.

    Якщо присутні лише шаблони ключових слів, вони обробляються таким чином, один за іншим:

    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.

    II. Коли всі позиційні моделі буде перетворено на ключові слова,

    відповідність відбувається так, якби були лише шаблони ключових слів.

    Для наступних вбудованих типів обробка позиційних підшаблонів відрізняється:

    These classes accept a single positional argument, and the pattern there is matched against the whole object rather than an attribute. For example int(0|1) matches the value 0, but not the value 0.0.

Простіше кажучи, CLS(P1, attr=P2) збігається, лише якщо відбувається таке:

  • isinstance( <subject> , CLS)

  • перетворити P1 на шаблон ключового слова за допомогою CLS.__match_args__

  • Для кожного аргументу ключового слова attr=P2:

    • hasattr( <subject> , "attr")

    • P2 відповідає <subject> .attr

  • … і так далі для відповідної пари ключового слова аргумент/шаблон.

Дивись також

  • PEP 634 – Зіставлення структурних шаблонів: Специфікація

  • PEP 636 – Зіставлення структурних шаблонів: підручник

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]

Визначення функції може бути обгорнуте одним або кількома виразами decorator. Вирази декоратора обчислюються, коли функція визначена в області видимості, яка містить визначення функції. Результат має бути викликаним, який викликається з об’єктом функції як єдиним аргументом. Повернене значення прив’язується до імені функції замість об’єкта функції. Кілька декораторів застосовуються вкладеним способом. Наприклад, такий код:

@f1(arg)
@f2
def func(): pass

приблизно еквівалентно

def func(): pass
func = f1(arg)(f2(func))

за винятком того, що вихідна функція тимчасово не прив’язана до імені func.

Змінено в версії 3.9: Функції можуть бути прикрашені будь-яким дійсним assignment_expression. Раніше граматика була набагато більш обмежувальною; подробиці див. PEP 614.

A list of type parameters may be given in square brackets between the function’s name and the opening parenthesis for its parameter list. This indicates to static type checkers that the function is generic. At runtime, the type parameters can be retrieved from the function’s __type_params__ attribute. See Generic functions for more.

Змінено в версії 3.12: Type parameter lists are new in Python 3.12.

Коли один або більше параметрів мають форму параметр = вираз, кажуть, що функція має «значення параметрів за замовчуванням». Для параметра зі значенням за замовчуванням відповідний argument може бути пропущений у виклику, у цьому випадку значення параметра за замовчуванням замінюється. Якщо параметр має значення за замовчуванням, усі наступні параметри до «*» також повинні мати значення за замовчуванням — це синтаксичне обмеження, яке не виражається граматикою.

Значення параметрів за замовчуванням обчислюються зліва направо, коли виконується визначення функції. Це означає, що вираз обчислюється один раз, коли визначається функція, і що те саме «попередньо обчислене» значення використовується для кожного виклику . Це особливо важливо розуміти, коли значення параметра за замовчуванням є змінним об’єктом, таким як список або словник: якщо функція змінює об’єкт (наприклад, шляхом додавання елемента до списку), значення параметра за замовчуванням фактично змінюється. Загалом це не те, що передбачалося. Щоб обійти це, використайте 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.

Параметри можуть мати анотацію у формі «:вираз» після імені параметра. Будь-який параметр може мати анотацію, навіть у формі *ідентифікатор або **ідентифікатор. Функції можуть мати анотацію «повернення» у формі «-> вираз» після списку параметрів. Ці анотації можуть бути будь-яким дійсним виразом Python. Наявність анотацій не змінює семантику функції. Значення анотацій доступні як значення словника, ключ якого містить імена параметрів в атрибуті __annotations__ об’єкта функції. Якщо використовується імпорт анотацій із __future__, анотації зберігаються як рядки під час виконання, що дає змогу відкласти оцінку. В іншому випадку вони оцінюються під час виконання визначення функції. У цьому випадку анотації можуть оцінюватися в іншому порядку, ніж у вихідному коді.

Також можна створювати анонімні функції (функції, не прив’язані до імені) для негайного використання у виразах. Тут використовуються лямбда-вирази, описані в розділі Лямбда. Зауважте, що лямбда-вираз — це лише скорочення спрощеного визначення функції; функція, визначена в операторі «def», може бути передана або присвоєна іншому імені так само, як функція, визначена лямбда-виразом. Форма «def» насправді є потужнішою, оскільки вона дозволяє виконувати кілька операторів і анотацій.

Примітка програміста: Функції є об’єктами першого класу. Оператор «def», який виконується всередині визначення функції, визначає локальну функцію, яку можна повертати або передавати. Вільні змінні, які використовуються у вкладеній функції, можуть отримати доступ до локальних змінних функції, що містить def. Перегляньте розділ Називання та зв’язування для деталей.

Дивись також

PEP 3107 - Анотації функцій

Оригінальна специфікація для анотацій функцій.

PEP 484 - підказки типу

Визначення стандартного значення для анотацій: тип підказок.

PEP 526 - Синтаксис для анотацій змінних

Ability to type hint variable declarations, including class variables and instance variables.

PEP 563 - Відкладена оцінка анотацій

Підтримка прямих посилань в анотаціях завдяки збереженню анотацій у формі рядка під час виконання замість нетерплячої оцінки.

PEP 318 - Decorators for Functions and Methods

Function and method decorators were introduced. Class decorators were introduced in 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__ нового класу. Зауважте, що це надійно лише відразу після створення класу та лише для класів, які були визначені за допомогою синтаксису визначення.

Створення класів можна значно налаштувати за допомогою metaclasses.

Класи також можна декорувати: як і при декоруванні функцій,

@f1(arg)
@f2
class Foo: pass

приблизно еквівалентно

class Foo: pass
Foo = f1(arg)(f2(Foo))

Правила оцінки для виразів декоратора такі ж, як і для декораторів функцій. Потім результат прив’язується до імені класу.

Змінено в версії 3.9: Класи можуть бути прикрашені будь-яким дійсним assignment_expression. Раніше граматика була набагато більш обмежувальною; подробиці див. PEP 614.

A list of type parameters may be given in square brackets immediately after the class’s name. This indicates to static type checkers that the class is generic. At runtime, the type parameters can be retrieved from the class’s __type_params__ attribute. See Generic classes for more.

Змінено в версії 3.12: Type parameter lists are new in Python 3.12.

Примітка програміста: Змінні, визначені у визначенні класу, є атрибутами класу; їх ділять інстанції. Атрибути екземпляра можна встановити в методі за допомогою self.name = value. Як атрибути класу, так і атрибути екземпляра доступні через нотацію «self.name», а атрибут екземпляра приховує атрибут класу з таким самим іменем, коли до нього звертаються таким чином. Атрибути класу можна використовувати як значення за замовчуванням для атрибутів екземплярів, але використання там змінних значень може призвести до неочікуваних результатів. Дескриптори можна використовувати для створення змінних екземпляра з різними деталями реалізації.

Дивись також

PEP 3115 - Метакласи в Python 3000

Пропозиція, яка змінила оголошення метакласів на поточний синтаксис і семантику того, як будуються класи з метакласами.

PEP 3129 - Декоратори класу

Пропозиція, яка додала декоратори класу. Декоратори функцій і методів були представлені в PEP 318.

8.9. Співпрограми

Added in version 3.5.

8.9.1. Визначення функції співпрограми

async_funcdef ::=  [decorators] "async" "def" funcname "(" [parameter_list] ")"
                   ["->" expression] ":" suite

Виконання співпрограм Python можна призупинити та відновити в багатьох точках (див. coroutine). Вирази 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

asynchronous iterable надає метод __aiter__, який безпосередньо повертає asynchronous iterator, який може викликати асинхронний код у своєму методі __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

See also __aiter__() and __anext__() for details.

Це SyntaxError, якщо використовувати оператор async for поза тілом функції співпрограми.

8.9.3. Оператор async with

async_with_stmt ::=  "async" with_stmt

Асинхронний менеджер контексту (asynchronous context manager) — це менеджер контексту (context manager),який може призупинити виконання своїх методів 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)

See also __aenter__() and __aexit__() for details.

Це SyntaxError, якщо використовувати оператор async with поза тілом функції співпрограми.

Дивись також

PEP 492 - Співпрограми з синтаксисом async і await

Пропозиція, яка зробила співпрограми справжньою автономною концепцією в Python і додала допоміжний синтаксис.

8.10. Type parameter lists

Added in version 3.12.

type_params  ::=  "[" type_param ("," type_param)* "]"
type_param   ::=  typevar | typevartuple | paramspec
typevar      ::=  identifier (":" expression)?
typevartuple ::=  "*" identifier
paramspec    ::=  "**" identifier

Functions (including coroutines), classes and type aliases may contain a type parameter list:

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]

Semantically, this indicates that the function, class, or type alias is generic over a type variable. This information is primarily used by static type checkers, and at runtime, generic objects behave much like their non-generic counterparts.

Type parameters are declared in square brackets ([]) immediately after the name of the function, class, or type alias. The type parameters are accessible within the scope of the generic object, but not elsewhere. Thus, after a declaration def func[T](): pass, the name T is not available in the module scope. Below, the semantics of generic objects are described with more precision. The scope of type parameters is modeled with a special function (technically, an annotation scope) that wraps the creation of the generic object.

Generic functions, classes, and type aliases have a __type_params__ attribute listing their type parameters.

Type parameters come in three kinds:

  • typing.TypeVar, introduced by a plain name (e.g., T). Semantically, this represents a single type to a type checker.

  • typing.TypeVarTuple, introduced by a name prefixed with a single asterisk (e.g., *Ts). Semantically, this stands for a tuple of any number of types.

  • typing.ParamSpec, introduced by a name prefixed with two asterisks (e.g., **P). Semantically, this stands for the parameters of a callable.

typing.TypeVar declarations can define bounds and constraints with a colon (:) followed by an expression. A single expression after the colon indicates a bound (e.g. T: int). Semantically, this means that the typing.TypeVar can only represent types that are a subtype of this bound. A parenthesized tuple of expressions after the colon indicates a set of constraints (e.g. T: (str, bytes)). Each member of the tuple should be a type (again, this is not enforced at runtime). Constrained type variables can only take on one of the types in the list of constraints.

For typing.TypeVars declared using the type parameter list syntax, the bound and constraints are not evaluated when the generic object is created, but only when the value is explicitly accessed through the attributes __bound__ and __constraints__. To accomplish this, the bounds or constraints are evaluated in a separate annotation scope.

typing.TypeVarTuples and typing.ParamSpecs cannot have bounds or constraints.

The following example indicates the full set of allowed type parameter declarations:

def overly_generic[
   SimpleTypeVar,
   TypeVarWithBound: int,
   TypeVarWithConstraints: (str, bytes),
   *SimpleTypeVarTuple,
   **SimpleParamSpec,
](
   a: SimpleTypeVar,
   b: TypeVarWithBound,
   c: Callable[SimpleParamSpec, TypeVarWithConstraints],
   *d: SimpleTypeVarTuple,
): ...

8.10.1. Generic functions

Generic functions are declared as follows:

def func[T](arg: T): ...

This syntax is equivalent to:

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()

Here annotation-def indicates an annotation scope, which is not actually bound to any name at runtime. (One other liberty is taken in the translation: the syntax does not go through attribute access on the typing module, but creates an instance of typing.TypeVar directly.)

The annotations of generic functions are evaluated within the annotation scope used for declaring the type parameters, but the function’s defaults and decorators are not.

The following example illustrates the scoping rules for these cases, as well as for additional flavors of type parameters:

@decorator
def func[T: int, *Ts, **P](*args: *Ts, arg: Callable[P, T] = some_default):
    ...

Except for the lazy evaluation of the TypeVar bound, this is equivalent to:

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())

The capitalized names like DEFAULT_OF_arg are not actually bound at runtime.

8.10.2. Generic classes

Generic classes are declared as follows:

class Bag[T]: ...

This syntax is equivalent to:

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()

Here again annotation-def (not a real keyword) indicates an annotation scope, and the name TYPE_PARAMS_OF_Bag is not actually bound at runtime.

Generic classes implicitly inherit from typing.Generic. The base classes and keyword arguments of generic classes are evaluated within the type scope for the type parameters, and decorators are evaluated outside that scope. This is illustrated by this example:

@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. Generic type aliases

The type statement can also be used to create a generic type alias:

type ListOrSet[T] = list[T] | set[T]

Except for the lazy evaluation of the value, this is equivalent to:

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()

Here, annotation-def (not a real keyword) indicates an annotation scope. The capitalized names like TYPE_PARAMS_OF_ListOrSet are not actually bound at runtime.

Виноски