PEP 585 нельзя использовать во время выполнения под Python 3.7 и 3.8? - PullRequest
8 голосов
/ 13 июля 2020

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, на самом деле не тестировал свою новую гипотетическую предлагаемую функциональность, прежде чем отказаться от существующей, хорошо протестированной функциональности, которая действительно работала. * работа из коробки. Все чаще они этого не делают. И это должно волновать всех.

1 Ответ

1 голос
/ 14 июля 2020

Я не уверен, почему вы публикуете это здесь, в StackOverflow? Если у вас есть отзыв о PEP, я думаю, вам лучше разместить его в списках рассылки python -dev или typing-sig.

Например, вы могли бы попытаться аргументировать это:

  1. Мы должны удалить псевдоконтейнеры позже, чем в 2025 году.
  2. Модуль typing_extensions должен быть обновлен, чтобы предоставить прокладки для list и dict, которые воспроизводят среду выполнения функциональность, которая будет добавлена ​​в Python 3.9.
  3. Было бы лучше заменить псевдо-контейнеры в typing псевдонимами встроенных функций, а не удалять их. (Я подозреваю, что это, скорее всего, будет тем, что в конечном итоге произойдет: например, см. это обсуждение в typing-sig )

... и так далее. Я уверен, что вы можете придумать больше идей, это были только те, которые я сейчас придумал.

В любом случае, я думаю, должно быть достаточно легко обрабатывать псевдо-контейнеры, удаляемые из typing, пока сохранение обратной совместимости. Например, вы можете попробовать:

  1. Написать инструмент для автоматического обновления вашего кода (при условии, что инструмент еще не был создан кем-то).

  2. Monkey-patching typing, чтобы добавить недостающее.

  3. Заблаговременное переключение на использование вашей собственной прокладки typing, которая делает что-то вроде следующего:

    from typing import *
    if sys.version_info >= (3, blah):
        List = list
        # etc
    

Конечно, эти подходы также не совсем чисты, но они делают позволяют достичь вашей цели по обеспечению полной обратной совместимости.

Подход «запретить самоанализ типов и переключаться на раннем этапе» также, вероятно, будет довольно разумным на практике: я подозреваю, что довольно большой процент кодовых баз использует подсказки типов исключительно для анализа типов stati c.

Но в более широком смысле, если вы хотите более беспроблемный самоанализ подсказок типов во время выполнения, я рекомендую вам подписаться на typing-sig или python -dev и оставлять свои отзывы всякий раз, когда предлагаются PEP, связанные с набором текста.

Пока что я не думаю, что кто-то достаточно заботился об интроспекции подсказок типов во время выполнения, чтобы делать что-либо, кроме обеспечения их базовой поддержки. Если вас не устраивает такой статус-кво, вы должны попытаться активизировать и отстаивать любые изменения, которые, по вашему мнению, необходимо внести.

В конце концов, Python - это проект, управляемый волонтерами. Так что, если вы хотите что-то изменить в Python, лучший способ сделать это - добровольно посвятить свое время и энергию, а не ждать, пока другие сделают это от вашего имени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...