PEP 585 - Generics подсказки типов в стандартных коллекциях заявляет об удобстве использования как в Python 3.7, так и в 3.8 со стандартной преамбулой from __future__ import annotations
. В частности:
Для случаев использования, ограниченных аннотациями типов, Python файлы с annotations
future-import (доступным с Python 3.7) могут параметризировать стандартные коллекции, включая встроенные.
Начиная с Python 3.7, когда используется from __future__ import annotations
, аннотации функций и переменных могут параметризовать стандартные коллекции напрямую. Пример:
from __future__ import annotations
def find(haystack: dict[str, list[int]]) -> int:
...
В то время как приведенный выше пример игрушки выполняет технический синтаксический анализ , это все, что он делает. Попытка фактически использовать параметризованную встроенную коллекцию во время выполнения в рамках Python 3.7 или 3.8 неизменно вызывает ужасное TypeError: 'type' object is not subscriptable
исключение:
>>> def find(haystack: dict[str, list[int]]) -> int: pass
>>> print(find.__annotations__)
{'haystack': 'dict[str, list[int]]', 'return': 'int'}
>>> eval(find.__annotations__['haystack'])
TypeError: 'type' object is not subscriptable
Обратите внимание, что оператор eval()
является стандартной идиомой для разрешения Отложенные аннотации в стиле PEP 563 во время выполнения . Даже не заставляйте меня начинать с PEP 563.
кому вы поверите: мне или вашему лживому PEP?
Это обескураживает набожных Питониста во мне. PEP 585 неоднократно заявляет, что сохраняет удобство использования во время выполнения :
Сохранение общего типа c во время выполнения позволяет самоанализ типа, который может использоваться для генерации API или проверки типов во время выполнения . Такое использование уже широко распространено.
Как и в случае с модулем typing
сегодня, параметризованные общие c типы, перечисленные в предыдущем разделе, сохраняют свои параметры типа во время выполнения. :
>>> list[str]
list[str]
>>> tuple[int, ...]
tuple[int, ...]
>>> ChainMap[str, list[str]]
collections.ChainMap[str, list[str]]
Конечно, ничего из вышеперечисленного не работает под Python 3.7 или 3.8 - независимо от того, включен ли from __future__ import annotations
или нет:
>>> list[str]
TypeError: 'type' object is not subscriptable
>>> tuple[int, ...]
TypeError: 'type' object is not subscriptable
>>> ChainMap[str, list[str]]
TypeError: 'type' object is not subscriptable
Итак PEP 585 явно нарушает все существующие попытки проанализировать общие c типы во время выполнения - особенно из средств проверки типов во время выполнения. Весь раздел «Параметры дженериков доступны во время выполнения» - это фарс.
Я упускаю что-то до боли очевидное или параметризованные встроенные коллекции представляют собой отравленную таблетку, которой они внешне кажутся? Поскольку оценка этих коллекций во время выполнения в Python 3.7 и 3.8 безусловно вызывает исключения, они непригодны для использования во время выполнения - делая их не просто бесполезными, но прямо вредными для широко распространенного варианта использования типа интроспекции и особенно типа времени выполнения
между камнем и жестким PEP
Любое указание типов в кодовой базе с параметризованными встроенными коллекциями будет принципиально несовместимо с средствами проверки типов во время выполнения в Python 3.7 и 3.8. Кодовые базы, предпочитающие время исполнения c проверке типов при сохранении обратной совместимости с Python <3.9 (который еще даже не был официально выпущен на момент написания этой статьи), поэтому не имеют другого выбора, кроме как полностью избегать параметризованных встроенных коллекций. </p>
Кроме того, это тоже невозможно. Почему? Поскольку PEP 585 не поддерживает всю иерархию typing
псевдоконтейнеров :
Импорт тех [например, typing.Tuple
, typing.List
, typing.Dict
] from typing
устарело. Из-за PEP 563 и намерения минимизировать влияние typing
на время выполнения, эта устаревшая версия не будет генерировать DeprecationWarnings
. Вместо этого средства проверки типов могут предупреждать о таком устаревшем использовании, когда целевая версия проверенной программы получает сигнал Python 3.9 или новее. Рекомендуется разрешить отключение этих предупреждений для всего проекта.
Устаревшие функции будут удалены из модуля typing
в первом Python версия, выпущенная через 5 лет после выпуска Python 3.9.0.
Рассмотрим, например, typing.Tuple[int]
. К 2025 году (или вскоре после этого) typing.Tuple
и, следовательно, typing.Tuple[int]
уйдет. Но tuple
нельзя безопасно параметризовать в Python 3.7 и 3.8, потому что это делает ваш проект несовместимым с всем, что анализирует типы. Так что tuple[int]
тоже не является жизнеспособным вариантом.
Значит, нет опций прямой и обратной совместимости. Вместо этого либо:
- Запретить интроспекцию типов (и, следовательно, проверку типов во время выполнения) полностью, просто предпочтя встроенные контейнеры (например,
tuple[int]
) псевдоконтейнерам typing
( например, typing.Tuple[int]
) или ... - Поддержка интроспекции типа (и, следовательно, проверки типа во время выполнения) одним из следующих способов:
- Предпочтение
typing
псевдоконтейнеры во встроенные контейнеры до 2025 года. В то время как рассматриваемый проект , так и все последующие проекты этого проекта необходимо будет реорганизовать следующим образом: - Drop Python Поддержка 3.7 и 3.8.
- Заменить все псевдоконтейнеры
typing
встроенными контейнерами.
- Немедленно отказаться от поддержки Python 3.7 и 3.8, предпочтя встроенные контейнеры
typing
псевдоконтейнеры. У этого есть неприятный недостаток - требуется нестабильный в настоящее время интерпретатор Python, но ... это технически вариант. Как-нибудь.
В 2020 году нет хороших вариантов - только спектр все более ужасающих меньших нескольких злых зол. Можно было бы надеяться, что авторы PEP действительно протестируют свои реализации во время выполнения. Тем не менее, вот и мы, без весла плывем по течению в дымящейся помойке созданных теорией анти-API. Добро пожаловать в Python.
но это еще не все
Технически существует третий способ. Это даже еще противно - но технически должно работать. Одно ужасное теоретическое построение заслуживает другого, я всегда говорю!
Поскольку отложенные аннотации, управляемые PEP 563, представляют собой просто строки, самоанализ типов может разумно запустить замену на основе регулярных выражений для каждого интроспектируемого типа. Для каждого типа, который является отложенной аннотацией, глобально замените каждую подстроку, ссылающуюся на параметризованный встроенный контейнер (например, list[str]
) в этой строке аннотации, на соответствующую подстроку, ссылающуюся на параметризованный псевдоконтейнер typing
(например, List[str]
).
Результат? Python 3.7- и 3.8-совместимая отложенная строка аннотации, которую можно безопасно оценивать до 2025 года, после чего внутреннюю замену (и Python 3.7 и поддержку 3.8) можно было бы просто отбросить.
Это полная ерунда -cray смехотворная скорость для звезд, но ... это будет вероятно, сработает. Основная проблема, конечно же, в том, что безумные хакеры не нужны только для того, чтобы соответствовать основным официальным PEP. Но за этой технической проблемой скрывается еще более глубокая культурная проблема. Никто - ни автор PEP 585, ни кто-либо из комментаторов, рецензирующих PEP 585, на самом деле не тестировал свою новую гипотетическую предлагаемую функциональность, прежде чем отказаться от существующей, хорошо протестированной функциональности, которая действительно работала. * работа из коробки. Все чаще они этого не делают. И это должно волновать всех.