Документация Python неофициальный перевод

annotationlib.md

453 строк · 56.2 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.15/library/annotationlib.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# `annotationlib` – Функциональность для интроспекции аннотаций89Добавлено в версии 3.14.1011**Исходный код:** [Lib/annotationlib.py](https://python-all.ru/src/3.15/Lib/annotationlib.py)1213---1415Модуль `annotationlib` предоставляет инструменты для интроспекции [аннотаций](https://python-all.ru/3.15/glossary.html#term-annotation) в модулях, классах и функциях.1617Аннотации [вычисляются лениво](https://python-all.ru/3.15/reference/executionmodel.html#lazy-evaluation) и часто содержат прямые ссылки на объекты, которые ещё не определены на момент создания аннотации. Этот модуль предоставляет набор низкоуровневых инструментов, которые можно использовать для надёжного получения аннотаций, даже при наличии прямых ссылок и других граничных случаев.1819Этот модуль поддерживает получение аннотаций в трёх основных форматах (см. [`Format`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format)), каждый из которых лучше всего подходит для разных случаев использования:2021- [`VALUE`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.VALUE) вычисляет аннотации и возвращает их значение. С ним проще всего работать, но он может вызывать ошибки, например, если аннотации содержат ссылки на неопределённые имена.22- [`FORWARDREF`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.FORWARDREF) возвращает объекты [`ForwardRef`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.ForwardRef) для аннотаций, которые не удаётся разрешить, что позволяет анализировать аннотации без их вычисления. Это полезно, когда нужно работать с аннотациями, которые могут содержать неразрешённые прямые ссылки.23- [`STRING`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.STRING) возвращает аннотации в виде строки, похожей на то, как они выглядят в исходном файле. Это полезно для генераторов документации, которые хотят отображать аннотации в читаемом виде.2425The [`get_annotations()`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.get_annotations) function is the main entry point for retrieving annotations. Given a function, class, or module, it returns an annotations dictionary in the requested format. This module also provides functionality for working directly with the [annotate function](https://python-all.ru/3.15/glossary.html#term-annotate-function) that is used to evaluate annotations, such as [`get_annotate_from_class_namespace()`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.get_annotate_from_class_namespace) and [`call_annotate_function()`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.call_annotate_function), as well as the [`call_evaluate_function()`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.call_evaluate_function) function for working with [evaluate functions](https://python-all.ru/3.15/glossary.html#term-evaluate-function).2627> **Внимание**28>29> Большинство функций этого модуля могут выполнять произвольный код; см. [раздел безопасности](https://python-all.ru/3.15/library/annotationlib.html#annotationlib-security) для получения дополнительной информации.3031> **См. также**32>33> [**PEP 649**](https://python-all.ru/3.15/library/annotationlib.html) предложил текущую модель работы аннотаций в Python.34>35> [**PEP 749**](https://python-all.ru/3.15/library/annotationlib.html) расширил различные аспекты [**PEP 649**](https://python-all.ru/3.15/library/annotationlib.html) и представил модуль `annotationlib`.36>37> [Лучшие практики по аннотациям](https://python-all.ru/3.15/howto/annotations.html#annotations-howto) описывает лучшие практики работы с аннотациями.38>39> [typing-extensions](https://python-all.ru/3.15/library/annotationlib.html) предоставляет обратный порт [`get_annotations()`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.get_annotations), который работает в более ранних версиях Python.4041## Семантика аннотаций4243Способ вычисления аннотаций менялся на протяжении истории Python 3, и в настоящее время всё ещё зависит от [future import](https://python-all.ru/3.15/reference/simple_stmts.html#future). Существовали следующие модели выполнения для аннотаций:4445- *Стандартная семантика* (по умолчанию в Python 3.0–3.13; см. [**PEP 3107**](https://python-all.ru/3.15/library/annotationlib.html) и [**PEP 526**](https://python-all.ru/3.15/library/annotationlib.html)): Аннотации вычисляются жадно по мере их встречи в исходном коде.46- *Строковые аннотации* (используются с `from __future__ import annotations` в Python 3.7 и новее; см. [**PEP 563**](https://python-all.ru/3.15/library/annotationlib.html)): Аннотации хранятся только в виде строк.47- *Отложенное вычисление* (по умолчанию в Python 3.14 и новее; см. [**PEP 649**](https://python-all.ru/3.15/library/annotationlib.html) и [**PEP 749**](https://python-all.ru/3.15/library/annotationlib.html)): Аннотации вычисляются лениво, только когда к ним обращаются.4849В качестве примера рассмотрим следующую программу:5051```python52def func(a: Cls) -> None:53    print(a)5455class Cls: pass5657print(func.__annotations__)58```5960Это будет вести себя следующим образом:6162- При стандартной семантике (Python 3.13 и ранее) будет выброшено [`NameError`](https://python-all.ru/3.15/library/exceptions.html#NameError) в строке, где определён `func`, поскольку `Cls` на тот момент является неопределённым именем.63- При строковых аннотациях (если используется `from __future__ import annotations`) будет напечатано `{'a': 'Cls', 'return': 'None'}`.64- При отложенном вычислении (Python 3.14 и новее) будет напечатано `{'a': <class 'Cls'>, 'return': None}`.6566Стандартная семантика использовалась, когда аннотации функций были впервые введены в Python 3.0 ([**PEP 3107**](https://python-all.ru/3.15/library/annotationlib.html)), так как это был самый простой и очевидный способ реализации аннотаций. Та же модель выполнения использовалась, когда аннотации переменных были введены в Python 3.6 ([**PEP 526**](https://python-all.ru/3.15/library/annotationlib.html)). Однако стандартная семантика вызывала проблемы при использовании аннотаций в качестве подсказок типов, например, необходимость ссылаться на имена, которые ещё не определены на момент встречи аннотации. Кроме того, были проблемы с производительностью при выполнении аннотаций во время импорта модуля. Поэтому в Python 3.7 [**PEP 563**](https://python-all.ru/3.15/library/annotationlib.html) ввёл возможность хранения аннотаций в виде строк с помощью синтаксиса `from __future__ import annotations`. Планировалось со временем сделать это поведение по умолчанию, но возникла проблема: строковые аннотации сложнее обрабатывать тем, кто анализирует аннотации во время выполнения. Альтернативное предложение, [**PEP 649**](https://python-all.ru/3.15/library/annotationlib.html), ввело третью модель выполнения – отложенное вычисление – и было реализовано в Python 3.14. Строковые аннотации всё ещё используются, если присутствует `from __future__ import annotations`, но это поведение будет со временем удалено.6768## Классы6970#### `class annotationlib.Format`7172[`IntEnum`](https://python-all.ru/3.15/library/enum.html#enum.IntEnum), описывающий форматы, в которых могут быть возвращены аннотации. Элементы перечисления или их эквивалентные целочисленные значения можно передавать в [`get_annotations()`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.get_annotations) и другие функции этого модуля, а также в функции [`__annotate__`](https://python-all.ru/3.15/reference/datamodel.html#object.__annotate__).7374#### `VALUE = 1`7576Значения – это результат вычисления выражений аннотаций.7778#### `VALUE_WITH_FAKE_GLOBALS = 2`7980Специальное значение, которое используется для сигнализации о том, что функция аннотации выполняется в специальном окружении с поддельными глобальными переменными. При передаче этого значения функции аннотации должны либо вернуть то же значение, что и для формата [`Format.VALUE`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.VALUE), либо вызвать [`NotImplementedError`](https://python-all.ru/3.15/library/exceptions.html#NotImplementedError), чтобы указать, что они не поддерживают выполнение в таком окружении. Этот формат используется только внутри и не должен передаваться функциям в этом модуле.8182#### `FORWARDREF = 3`8384Значения – это реальные значения аннотаций (согласно формату [`Format.VALUE`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.VALUE)) для определённых значений и прокси [`ForwardRef`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.ForwardRef) для неопределённых. Реальные объекты могут содержать ссылки на прокси-объекты `ForwardRef`.8586#### `STRING = 4`8788Значения представляют собой текстовую строку аннотации в том виде, в каком она встречается в исходном коде, с учётом модификаций, включая, но не ограничиваясь, нормализацию пробелов и оптимизацию константных значений.8990Точные значения этих строк могут измениться в будущих версиях Python.9192Добавлено в версии 3.14.9394#### `class annotationlib.ForwardRef`9596Прокси-объект для прямых ссылок в аннотациях.9798Экземпляры этого класса возвращаются, когда используется формат [`FORWARDREF`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.FORWARDREF) и аннотации содержат имя, которое не удаётся разрешить. Это может произойти, когда в аннотации используется прямая ссылка, например, когда класс упоминается до его определения.99100#### `__forward_arg__`101102Строка, содержащая код, который был вычислен для получения `ForwardRef`. Строка может не полностью соответствовать исходному коду.103104#### `evaluate(*, owner=None, globals=None, locals=None, type_params=None, format=Format.VALUE)`105106Вычислить прямую ссылку и вернуть её значение.107108Если аргумент *format* равен [`VALUE`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.VALUE) (по умолчанию), этот метод может выбросить исключение, например [`NameError`](https://python-all.ru/3.15/library/exceptions.html#NameError), если прямая ссылка указывает на имя, которое не удаётся разрешить. Аргументы этого метода можно использовать для предоставления привязок для имён, которые в противном случае были бы не определены. Если аргумент *format* равен [`FORWARDREF`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.FORWARDREF), метод никогда не выбросит исключение, но может вернуть экземпляр `ForwardRef`. Например, если объект прямой ссылки содержит код `list[undefined]`, где `undefined` – это имя, которое не определено, вычисление с форматом `FORWARDREF` вернёт `list[ForwardRef('undefined')]`. Если аргумент *format* равен [`STRING`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.STRING), метод вернёт [`__forward_arg__`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.ForwardRef.__forward_arg__).109110Параметр *owner* предоставляет предпочтительный механизм для передачи информации об области видимости этому методу. Владелец `ForwardRef` – это объект, который содержит аннотацию, из которой получен `ForwardRef`, например объект модуля, типа или функции.111112Параметры *globals*, *locals* и *type\_params* предоставляют более точный механизм для управления именами, доступными при вычислении `ForwardRef`. *globals* и *locals* передаются в [`eval()`](https://python-all.ru/3.15/library/functions.html#eval) и представляют глобальное и локальное пространства имён, в которых вычисляется имя. Параметр *type\_params* актуален для объектов, созданных с помощью нативного синтаксиса для [обобщённых классов](https://python-all.ru/3.15/reference/compound_stmts.html#generic-classes) и [функций](https://python-all.ru/3.15/reference/compound_stmts.html#generic-functions). Это кортеж [параметров типа](https://python-all.ru/3.15/reference/compound_stmts.html#type-params), которые находятся в области видимости при вычислении прямой ссылки. Например, при вычислении `ForwardRef`, полученного из аннотации, найденной в пространстве имён класса обобщённого класса `C`, *type\_params* должен быть установлен в `C.__type_params__`.113114Экземпляры `ForwardRef`, возвращаемые [`get_annotations()`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.get_annotations), сохраняют ссылки на информацию об области видимости, из которой они произошли, поэтому вызов этого метода без дополнительных аргументов может быть достаточным для вычисления таких объектов. Экземпляры `ForwardRef`, созданные другими способами, могут не иметь информации о своей области видимости, поэтому для их успешного вычисления может потребоваться передача аргументов этому методу.115116Если не указаны *owner*, *globals*, *locals* или *type\_params*, и `ForwardRef` не содержит информации о своём происхождении, используются пустые словари globals и locals.117118Добавлено в версии 3.14.119120## Функции121122#### `annotationlib.annotations_to_string(annotations)`123124Преобразует словарь аннотаций, содержащий значения времени выполнения, в словарь, содержащий только строки. Если значения ещё не являются строками, они преобразуются с помощью [`type_repr()`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.type_repr). Это предназначено в качестве помощника для пользовательских функций аннотации, которые поддерживают формат [`STRING`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.STRING), но не имеют доступа к коду, создающему аннотации.125126Например, это используется для реализации [`STRING`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.STRING) для классов [`typing.TypedDict`](https://python-all.ru/3.15/library/typing.html#typing.TypedDict), созданных с помощью функционального синтаксиса:127128```pycon129>>> from typing import TypedDict130>>> Movie = TypedDict("movie", {"name": str, "year": int})131>>> get_annotations(Movie, format=Format.STRING)132{'name': 'str', 'year': 'int'}133```134135Добавлено в версии 3.14.136137#### `annotationlib.call_annotate_function(annotate, format, *, owner=None)`138139Вызывает [функцию аннотации](https://python-all.ru/3.15/glossary.html#term-annotate-function) *annotate* с заданным *format*, членом перечисления [`Format`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format), и возвращает словарь аннотаций, полученный от функции.140141Эта вспомогательная функция необходима, потому что функции аннотации, сгенерированные компилятором для функций, классов и модулей, при прямом вызове поддерживают только формат [`VALUE`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.VALUE). Для поддержки других форматов эта функция вызывает функцию аннотации в специальном окружении, позволяющем создавать аннотации в других форматах. Это полезный строительный блок при реализации функциональности, требующей частичного вычисления аннотаций во время конструирования класса.142143*owner* – это объект, владеющий функцией аннотации, обычно функция, класс или модуль. Если указан, он используется в формате [`FORWARDREF`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.FORWARDREF) для создания объекта [`ForwardRef`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.ForwardRef), несущего больше информации.144145> **См. также**146>147> [**PEP 649**](https://python-all.ru/3.15/library/annotationlib.html) содержит объяснение техники реализации, используемой этой функцией.148149Добавлено в версии 3.14.150151#### `annotationlib.call_evaluate_function(evaluate, format, *, owner=None)`152153Вызывает [функцию evaluate](https://python-all.ru/3.15/glossary.html#term-evaluate-function) *evaluate* с заданным *format*, членом перечисления [`Format`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format), и возвращает значение, полученное от функции. Это похоже на [`call_annotate_function()`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.call_annotate_function), но последняя всегда возвращает словарь, отображающий строки в аннотации, в то время как эта функция возвращает одно значение.154155Предназначена для использования с функциями evaluate, сгенерированными для лениво вычисляемых элементов, связанных с псевдонимами типов и параметрами типа:156157- [`typing.TypeAliasType.evaluate_value()`](https://python-all.ru/3.15/library/typing.html#typing.TypeAliasType.evaluate_value), значение псевдонимов типа158- [`typing.TypeVar.evaluate_bound()`](https://python-all.ru/3.15/library/typing.html#typing.TypeVar.evaluate_bound), граница переменных типа159- [`typing.TypeVar.evaluate_constraints()`](https://python-all.ru/3.15/library/typing.html#typing.TypeVar.evaluate_constraints), ограничения переменных типа160- [`typing.TypeVar.evaluate_default()`](https://python-all.ru/3.15/library/typing.html#typing.TypeVar.evaluate_default), значение по умолчанию переменных типа161- [`typing.ParamSpec.evaluate_default()`](https://python-all.ru/3.15/library/typing.html#typing.ParamSpec.evaluate_default), значение по умолчанию спецификаций параметров162- [`typing.TypeVarTuple.evaluate_default()`](https://python-all.ru/3.15/library/typing.html#typing.TypeVarTuple.evaluate_default), значение по умолчанию кортежей переменных типа163164*owner* – это объект, которому принадлежит функция evaluate, например, объект псевдонима типа или переменной типа.165166*format* можно использовать для управления форматом, в котором возвращается значение:167168```pycon169>>> type Alias = undefined170>>> call_evaluate_function(Alias.evaluate_value, Format.VALUE)171Traceback (most recent call last):172...173NameError: name 'undefined' is not defined174>>> call_evaluate_function(Alias.evaluate_value, Format.FORWARDREF)175ForwardRef('undefined')176>>> call_evaluate_function(Alias.evaluate_value, Format.STRING)177'undefined'178```179180Добавлено в версии 3.14.181182#### `annotationlib.get_annotate_from_class_namespace(namespace)`183184Извлекает [функцию annotate](https://python-all.ru/3.15/glossary.html#term-annotate-function) из словаря пространства имён класса *namespace*. Возвращает `None`, если пространство имён не содержит функцию annotate. Это полезно в первую очередь до того, как класс был полностью создан (например, в метаклассе); после того как класс существует, функцию annotate можно получить с помощью `cls.__annotate__`. См. [ниже](https://python-all.ru/3.15/library/annotationlib.html#annotationlib-metaclass) пример использования этой функции в метаклассе.185186Добавлено в версии 3.14.187188#### `annotationlib.get_annotations(obj, *, globals=None, locals=None, eval_str=False, format=Format.VALUE)`189190Вычисляет словарь аннотаций для объекта.191192*obj* может быть вызываемым объектом, классом, модулем или другим объектом с атрибутами [`__annotate__`](https://python-all.ru/3.15/reference/datamodel.html#object.__annotate__) или [`__annotations__`](https://python-all.ru/3.15/reference/datamodel.html#object.__annotations__). Передача любого другого объекта вызывает [`TypeError`](https://python-all.ru/3.15/library/exceptions.html#TypeError).193194Параметр *format* управляет форматом, в котором возвращаются аннотации, и должен быть членом перечисления [`Format`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format) или его целочисленным эквивалентом. Различные форматы работают следующим образом:195196- VALUE: сначала пробуется `object.__annotations__`; если его нет, вызывается функция `object.__annotate__`, если она существует.197- FORWARDREF: Если `object.__annotations__` существует и может быть вычислено успешно, оно используется; в противном случае вызывается функция `object.__annotate__`. Если и её не существует, снова пробуется `object.__annotations__`, и любая ошибка при доступе к нему возбуждается повторно.198199  - При вызове `object.__annotate__` сначала он вызывается с [`FORWARDREF`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.FORWARDREF). Если это не реализовано, затем проверяется, поддерживается ли [`VALUE_WITH_FAKE_GLOBALS`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.VALUE_WITH_FAKE_GLOBALS), и используется в поддельном окружении globals. Если ни один из этих форматов не поддерживается, используется [`VALUE`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.VALUE). Если `VALUE` завершается ошибкой, возбуждается ошибка от этого вызова.200- STRING: Если `object.__annotate__` существует, сначала вызывается он; в противном случае используется `object.__annotations__` и преобразуется в строку с помощью [`annotations_to_string()`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.annotations_to_string).201202  - При вызове `object.__annotate__` сначала он вызывается с [`STRING`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.STRING). Если это не реализовано, затем проверяется, поддерживается ли [`VALUE_WITH_FAKE_GLOBALS`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.VALUE_WITH_FAKE_GLOBALS), и используется в поддельном окружении globals. Если ни один из этих форматов не поддерживается, используется [`VALUE`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.VALUE), а результат преобразуется с помощью [`annotations_to_string()`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.annotations_to_string). Если `VALUE` завершается ошибкой, возбуждается ошибка от этого вызова.203204Возвращает словарь. `get_annotations()` возвращает новый словарь при каждом вызове; вызов его дважды для одного и того же объекта вернёт два разных, но эквивалентных словаря.205206Эта функция обрабатывает несколько деталей:207208- Если *eval\_str* истинно, значения типа `str` будут преобразованы из строки с помощью [`eval()`](https://python-all.ru/3.15/library/functions.html#eval). Это предназначено для использования со строковыми аннотациями (`from __future__ import annotations`). Ошибка – устанавливать *eval\_str* в true с форматами, отличными от [`Format.VALUE`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.VALUE).209- Если *obj* не имеет словаря аннотаций, возвращается пустой словарь. (Функции и методы всегда имеют словарь аннотаций; классы, модули и другие типы вызываемых объектов – не обязательно.)210- Игнорирует унаследованные аннотации в классах, а также аннотации в метаклассах. Если класс не имеет собственного словаря аннотаций, возвращается пустой словарь.211- Все обращения к членам объекта и значениям словаря выполняются с использованием `getattr()` и `dict.get()` для безопасности.212213*eval\_str* управляет тем, заменяются ли значения типа `str` результатом вызова [`eval()`](https://python-all.ru/3.15/library/functions.html#eval) для этих значений:214215- Если eval\_str истинно, [`eval()`](https://python-all.ru/3.15/library/functions.html#eval) вызывается для значений типа `str`. (Обратите внимание, что `get_annotations()` не перехватывает исключения; если `eval()` возбуждает исключение, оно раскрутит стек за пределы вызова `get_annotations()`.)216- Если *eval\_str* ложно (по умолчанию), значения типа `str` остаются без изменений.217218*globals* и *locals* передаются в [`eval()`](https://python-all.ru/3.15/library/functions.html#eval); см. документацию `eval()` для получения дополнительной информации. Если *globals* или *locals* равно `None`, эта функция может заменить это значение на контекстно-зависимое значение по умолчанию, в зависимости от `type(obj)`:219220- Если *obj* – это модуль, *globals* по умолчанию равно `obj.__dict__`.221- Если *obj* – это класс, *globals* по умолчанию равно `sys.modules[obj.__module__].__dict__`, а *locals* по умолчанию равно пространству имён класса *obj*.222- Если *obj* – вызываемый объект, *globals* по умолчанию равно [`obj.__globals__`](https://python-all.ru/3.15/reference/datamodel.html#function.__globals__), однако если *obj* является обёрнутой функцией (с помощью [`functools.update_wrapper()`](https://python-all.ru/3.15/library/functools.html#functools.update_wrapper)) или объектом [`functools.partial`](https://python-all.ru/3.15/library/functools.html#functools.partial), он разворачивается до тех пор, пока не будет найдена необёрнутая функция.223224Вызов `get_annotations()` является лучшей практикой для доступа к словарю аннотаций любого объекта. См. [Лучшие практики аннотаций](https://python-all.ru/3.15/howto/annotations.html#annotations-howto) для получения дополнительной информации о лучших практиках аннотаций.225226```pycon227>>> def f(a: int, b: str) -> float:228...     pass229>>> get_annotations(f)230{'a': <class 'int'>, 'b': <class 'str'>, 'return': <class 'float'>}231```232233Добавлено в версии 3.14.234235#### `annotationlib.type_repr(value)`236237Преобразует произвольное значение Python в формат, пригодный для использования форматом [`STRING`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.STRING). Для большинства объектов вызывает [`repr()`](https://python-all.ru/3.15/library/functions.html#repr), но для некоторых объектов, например объектов-типов, применяется специальная обработка.238239Это предназначено в качестве вспомогательного средства для предоставляемых пользователем аннотирующих функций, которые поддерживают формат [`STRING`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.STRING), но не имеют доступа к коду, создающему аннотации. Его также можно использовать для предоставления удобочитаемого строкового представления для других объектов, содержащих значения, часто встречающиеся в аннотациях.240241Добавлено в версии 3.14.242243## Рецепты244245### Использование аннотаций в метаклассе246247[Метакласс](https://python-all.ru/3.15/reference/datamodel.html#metaclasses) может захотеть проверить или даже изменить аннотации в теле класса во время его создания. Для этого требуется извлечь аннотации из словаря пространства имён класса. Для классов, созданных с помощью `from __future__ import annotations`, аннотации будут находиться по ключу `__annotations__` словаря. Для других классов с аннотациями можно использовать [`get_annotate_from_class_namespace()`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.get_annotate_from_class_namespace) для получения аннотирующей функции и [`call_annotate_function()`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.call_annotate_function) для её вызова и получения аннотаций. Обычно лучше всего использовать формат [`FORWARDREF`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.FORWARDREF), поскольку он позволяет аннотациям ссылаться на имена, которые ещё не могут быть разрешены на момент создания класса.248249Для изменения аннотаций лучше всего создать функцию-обёртку для аннотирования, которая вызывает исходную аннотирующую функцию, вносит необходимые изменения и возвращает результат.250251Ниже приведён пример метакласса, который отфильтровывает все аннотации [`typing.ClassVar`](https://python-all.ru/3.15/library/typing.html#typing.ClassVar) из класса и помещает их в отдельный атрибут:252253```python254import annotationlib255import typing256257class ClassVarSeparator(type):258   def __new__(mcls, name, bases, ns):259      if "__annotations__" in ns:  # from __future__ import annotations260         annotations = ns["__annotations__"]261         classvar_keys = {262            key for key, value in annotations.items()263            # Для простоты используется строковое сравнение; более надёжное решение264            # может использовать annotationlib.ForwardRef.evaluate265            if value.startswith("ClassVar")266         }267         classvars = {key: annotations[key] for key in classvar_keys}268         ns["__annotations__"] = {269            key: value for key, value in annotations.items()270            if key not in classvar_keys271         }272         wrapped_annotate = None273      elif annotate := annotationlib.get_annotate_from_class_namespace(ns):274         annotations = annotationlib.call_annotate_function(275            annotate, format=annotationlib.Format.FORWARDREF276         )277         classvar_keys = {278            key for key, value in annotations.items()279            if typing.get_origin(value) is typing.ClassVar280         }281         classvars = {key: annotations[key] for key in classvar_keys}282283         def wrapped_annotate(format):284            annos = annotationlib.call_annotate_function(annotate, format, owner=typ)285            return {key: value for key, value in annos.items() if key not in classvar_keys}286287      else:  # нет аннотаций288         classvars = {}289         wrapped_annotate = None290      typ = super().__new__(mcls, name, bases, ns)291292      if wrapped_annotate is not None:293         # Оборачивается оригинальный __annotate__ в обёртку, удаляющую ClassVars294         typ.__annotate__ = wrapped_annotate295      typ.classvars = classvars  # ClassVars сохраняются в отдельном атрибуте296      return typ297```298299### Создание пользовательской вызываемой аннотирующей функции300301Пользовательские [аннотирующие функции](https://python-all.ru/3.15/glossary.html#term-annotate-function) могут быть буквальными функциями, как те, что автоматически генерируются для функций, классов и модулей. Или же они могут захотеть использовать инкапсуляцию, предоставляемую классами; в этом случае в качестве аннотирующей функции может использоваться любой [вызываемый объект](https://python-all.ru/3.15/glossary.html#term-callable).302303Чтобы напрямую предоставлять форматы [`VALUE`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.VALUE), [`STRING`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.STRING) или [`FORWARDREF`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.FORWARDREF), [аннотирующая функция](https://python-all.ru/3.15/glossary.html#term-annotate-function) должна предоставлять следующий атрибут:304305- Вызываемый объект `__call__` с сигнатурой `__call__(format, /) -> dict`, который не вызывает исключение [`NotImplementedError`](https://python-all.ru/3.15/library/exceptions.html#NotImplementedError) при вызове с поддерживаемым форматом.306307Чтобы предоставлять формат [`VALUE_WITH_FAKE_GLOBALS`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.VALUE_WITH_FAKE_GLOBALS), который используется для автоматической генерации [`STRING`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.STRING) или [`FORWARDREF`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.FORWARDREF), если они не поддерживаются напрямую, [аннотирующие функции](https://python-all.ru/3.15/glossary.html#term-annotate-function) должны предоставлять следующие атрибуты:308309- Вызываемый объект `__call__` с сигнатурой `__call__(format, /) -> dict`, который не вызывает исключение [`NotImplementedError`](https://python-all.ru/3.15/library/exceptions.html#NotImplementedError) при вызове с [`VALUE_WITH_FAKE_GLOBALS`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.VALUE_WITH_FAKE_GLOBALS).310- [Объект кода](https://python-all.ru/3.15/reference/datamodel.html#code-objects) `__code__`, содержащий скомпилированный код для аннотирующей функции.311- Необязательно: кортеж позиционных значений по умолчанию функции `__kwdefaults__`, если функция, представленная `__code__`, использует какие-либо позиционные значения по умолчанию.312- Необязательно: словарь именованных значений по умолчанию функции `__defaults__`, если функция, представленная `__code__`, использует какие-либо именованные значения по умолчанию.313- Необязательно: все остальные [атрибуты функции](https://python-all.ru/3.15/library/inspect.html#inspect-types).314315```python316class Annotate:317    called_formats = []318319    def __call__(self, format=None, /, *, _self=None):320        # При вызове с fake globals, `_self` будет321        # фактическим значением self, а `self` будет форматом.322        if _self is not None:323            self, format = _self, self324325        self.called_formats.append(format)326        if format <= 2:  # VALUE или VALUE_WITH_FAKE_GLOBALS327            return {"x": MyType}328        raise NotImplementedError329330    __code__ = __call__.__code__331    __defaults__ = (None,)332    __kwdefaults__ = property(lambda self: dict(_self=self))333334    __globals__ = {}335    __builtins__ = {}336    __closure__ = None337```338339Затем это можно вызвать с помощью:340341```pycon342>>> from annotationlib import call_annotate_function, Format343>>> call_annotate_function(Annotate(), format=Format.STRING)344{'x': 'MyType'}345```346347Или использовать в качестве аннотирующей функции для объекта:348349```pycon350>>> from annotationlib import get_annotations, Format351>>> class C:352...   pass353>>> C.__annotate__ = Annotate()354>>> get_annotations(Annotate(), format=Format.STRING)355{'x': 'MyType'}356```357358## Ограничения формата `STRING`359360Формат [`STRING`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.STRING) предназначен для приближения исходного кода аннотации, но используемая стратегия реализации означает, что не всегда возможно восстановить точный исходный код.361362Во-первых, преобразователь в строку, конечно, не может восстановить информацию, отсутствующую в скомпилированном коде, включая комментарии, пробелы, расстановку скобок и операции, которые упрощаются компилятором.363364Во-вторых, преобразователь в строку может перехватить почти все операции, связанные с поиском имён в некоторой области видимости, но не может перехватить операции, полностью работающие с константами. Как следствие, это также означает, что небезопасно запрашивать формат `STRING` для недоверенного кода: Python достаточно мощен, чтобы можно было добиться произвольного выполнения кода даже без доступа к глобальным или встроенным объектам. Например:365366```pycon367>>> def f(x: (1).__class__.__base__.__subclasses__()[-1].__init__.__builtins__["print"]("Hello world")): pass368...369>>> annotationlib.get_annotations(f, format=annotationlib.Format.STRING)370Hello world371{'x': 'None'}372```373374> **Примечание**375>376> Этот конкретный пример работает на момент написания, но он опирается на детали реализации и не гарантирует работу в будущем.377378Среди различных типов выражений, существующих в Python, представленных модулем [`ast`](https://python-all.ru/3.15/library/ast.html#module-ast), одни выражения поддерживаются, то есть формат `STRING` обычно может восстановить исходный код; другие не поддерживаются, то есть могут привести к некорректному выводу или ошибке.379380Следующие выражения поддерживаются (иногда с оговорками):381382- [`ast.BinOp`](https://python-all.ru/3.15/library/ast.html#ast.BinOp)383- [`ast.UnaryOp`](https://python-all.ru/3.15/library/ast.html#ast.UnaryOp)384385  - [`ast.Invert`](https://python-all.ru/3.15/library/ast.html#ast.Invert) (`~`), [`ast.UAdd`](https://python-all.ru/3.15/library/ast.html#ast.UAdd) (`+`) и [`ast.USub`](https://python-all.ru/3.15/library/ast.html#ast.USub) (`-`) поддерживаются386  - [`ast.Not`](https://python-all.ru/3.15/library/ast.html#ast.Not) (`not`) не поддерживается387- [`ast.Dict`](https://python-all.ru/3.15/library/ast.html#ast.Dict) (за исключением случаев использования `**`)388- [`ast.Set`](https://python-all.ru/3.15/library/ast.html#ast.Set)389- [`ast.Compare`](https://python-all.ru/3.15/library/ast.html#ast.Compare)390391  - [`ast.Eq`](https://python-all.ru/3.15/library/ast.html#ast.Eq) и [`ast.NotEq`](https://python-all.ru/3.15/library/ast.html#ast.NotEq) поддерживаются392  - [`ast.Lt`](https://python-all.ru/3.15/library/ast.html#ast.Lt), [`ast.LtE`](https://python-all.ru/3.15/library/ast.html#ast.LtE), [`ast.Gt`](https://python-all.ru/3.15/library/ast.html#ast.Gt) и [`ast.GtE`](https://python-all.ru/3.15/library/ast.html#ast.GtE) поддерживаются, но операнд может быть переставлен393  - [`ast.Is`](https://python-all.ru/3.15/library/ast.html#ast.Is), [`ast.IsNot`](https://python-all.ru/3.15/library/ast.html#ast.IsNot), [`ast.In`](https://python-all.ru/3.15/library/ast.html#ast.In) и [`ast.NotIn`](https://python-all.ru/3.15/library/ast.html#ast.NotIn) не поддерживаются394- [`ast.Call`](https://python-all.ru/3.15/library/ast.html#ast.Call) (за исключением случаев использования `**`)395- [`ast.Constant`](https://python-all.ru/3.15/library/ast.html#ast.Constant) (хотя это не точное представление константы; например, escape-последовательности в строках теряются; шестнадцатеричные числа преобразуются в десятичные)396- [`ast.Attribute`](https://python-all.ru/3.15/library/ast.html#ast.Attribute) (если значение не является константой)397- [`ast.Subscript`](https://python-all.ru/3.15/library/ast.html#ast.Subscript) (если значение не является константой)398- [`ast.Starred`](https://python-all.ru/3.15/library/ast.html#ast.Starred) (распаковка `*`)399- [`ast.Name`](https://python-all.ru/3.15/library/ast.html#ast.Name)400- [`ast.List`](https://python-all.ru/3.15/library/ast.html#ast.List)401- [`ast.Tuple`](https://python-all.ru/3.15/library/ast.html#ast.Tuple)402- [`ast.Slice`](https://python-all.ru/3.15/library/ast.html#ast.Slice)403404Следующее не поддерживается, но при обнаружении преобразователем в строку выдается информативное исключение:405406- [`ast.FormattedValue`](https://python-all.ru/3.15/library/ast.html#ast.FormattedValue) (f-строки; ошибка не обнаруживается, если используются спецификаторы преобразования, такие как `!r`)407- [`ast.JoinedStr`](https://python-all.ru/3.15/library/ast.html#ast.JoinedStr) (f-строки)408409Следующее не поддерживается и приводит к некорректному выводу:410411- [`ast.BoolOp`](https://python-all.ru/3.15/library/ast.html#ast.BoolOp) (`and` и `or`)412- [`ast.IfExp`](https://python-all.ru/3.15/library/ast.html#ast.IfExp)413- [`ast.Lambda`](https://python-all.ru/3.15/library/ast.html#ast.Lambda)414- [`ast.ListComp`](https://python-all.ru/3.15/library/ast.html#ast.ListComp)415- [`ast.SetComp`](https://python-all.ru/3.15/library/ast.html#ast.SetComp)416- [`ast.DictComp`](https://python-all.ru/3.15/library/ast.html#ast.DictComp)417- [`ast.GeneratorExp`](https://python-all.ru/3.15/library/ast.html#ast.GeneratorExp)418419Следующее запрещено в области аннотаций и поэтому не имеет значения:420421- [`ast.NamedExpr`](https://python-all.ru/3.15/library/ast.html#ast.NamedExpr) (`:=`)422- [`ast.Await`](https://python-all.ru/3.15/library/ast.html#ast.Await)423- [`ast.Yield`](https://python-all.ru/3.15/library/ast.html#ast.Yield)424- [`ast.YieldFrom`](https://python-all.ru/3.15/library/ast.html#ast.YieldFrom)425426## Ограничения формата `FORWARDREF`427428Формат [`FORWARDREF`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.FORWARDREF) стремится по мере возможности создавать реальные значения, а всё, что не удаётся разрешить, заменяется объектами [`ForwardRef`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.ForwardRef). На него распространяются в целом те же ограничения, что и на формат [`STRING`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.Format.STRING): аннотации, выполняющие операции над литералами или использующие неподдерживаемые типы выражений, могут вызывать исключения при вычислении с помощью формата `FORWARDREF`.429430Ниже приведены несколько примеров поведения с неподдерживаемыми выражениями:431432```pycon433>>> from annotationlib import get_annotations, Format434>>> def zerodiv(x: 1 / 0): ...435>>> get_annotations(zerodiv, format=Format.STRING)436Traceback (most recent call last):437  ...438ZeroDivisionError: division by zero439>>> get_annotations(zerodiv, format=Format.FORWARDREF)440Traceback (most recent call last):441  ...442ZeroDivisionError: division by zero443>>> def ifexp(x: 1 if y else 0): ...444>>> get_annotations(ifexp, format=Format.STRING)445{'x': '1'}446```447448## Последствия для безопасности при интроспекции аннотаций449450Большая часть функциональности этого модуля связана с выполнением кода, относящегося к аннотациям, который затем может делать произвольные вещи. Например, [`get_annotations()`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.get_annotations) может вызвать произвольную [функцию annotate](https://python-all.ru/3.15/glossary.html#term-annotate-function), а [`ForwardRef.evaluate()`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.ForwardRef.evaluate) может вызвать [`eval()`](https://python-all.ru/3.15/library/functions.html#eval) для произвольной строки. Код, содержащийся в аннотации, может выполнять произвольные системные вызовы, входить в бесконечный цикл или выполнять любые другие операции. Это также верно для любого доступа к атрибуту [`__annotations__`](https://python-all.ru/3.15/reference/datamodel.html#object.__annotations__) и для различных функций в модуле [`typing`](https://python-all.ru/3.15/library/typing.html#module-typing), работающих с аннотациями, таких как [`typing.get_type_hints()`](https://python-all.ru/3.15/library/typing.html#typing.get_type_hints).451452Любая проблема безопасности, возникающая из-за этого, также применяется сразу после импорта кода, который может содержать ненадёжные аннотации: импорт кода всегда может привести к выполнению произвольных операций. Однако небезопасно принимать строки или другие входные данные из ненадёжного источника и передавать их любому из API для интроспекции аннотаций, например, редактируя словарь `__annotations__` или напрямую создавая объект [`ForwardRef`](https://python-all.ru/3.15/library/annotationlib.html#annotationlib.ForwardRef).453