Я пытаюсь найти надежный / кросс-версия (3.5+) способ проверки того, является ли аннотация типа «подклассом» данного универсального типа (т.е. получить универсальный тип из объекта аннотации типа).
В Python 3.5 / 3.6 он работает на одном дыхании, как и следовало ожидать:
>>> from typing import List
>>> isinstance(List[str], type)
True
>>> issubclass(List[str], List)
True
В то время как на 3.7 похоже, что экземпляры универсальных типов больше не являются экземплярами type
, поэтому он потерпит неудачу:
>>> from typing import List
>>> isinstance(List[str], type)
False
>>> issubclass(List[str], List)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.7/typing.py", line 716, in __subclasscheck__
raise TypeError("Subscripted generics cannot be used with"
TypeError: Subscripted generics cannot be used with class and instance checks
Другие идеи, которые приходят на ум, проверяют фактический тип экземпляра, но:
Python 3.6 / 3.5:
>>> type(List[str])
<class 'typing.GenericMeta'>
Python 3.7:
>>> type(List[str])
<class 'typing._GenericAlias'>
Но это на самом деле не дает никаких дополнительных указаний относительно того, какой фактический универсальный тип является действительным (возможно, это не List);кроме того, кажется, что делать проверку таким образом неправильно, тем более что _GenericAlias
теперь стал «закрытым» типом (обратите внимание на подчеркивание).
Еще одна вещь, которую можно проверить, это аргумент __origin__
на тип, но это не похоже на правильный способ сделать это либо.
И он по-прежнему отличается на 3,7:
>>> List[str].__origin__
<class 'list'>
в то время как 3,5 / 3,6:
>>> List[str].__origin__
typing.List
Я искал "правильный" способ сделать это, но не нашел его в поиске документов / Google Python.
Теперь я предполагаю, что должен бытьчистый способ сделать эту проверку, так как такие инструменты, как mypy, будут полагаться на него при выполнении проверок типов ..?
Обновление: об сценарии использования
Хорошо, добавьте немного больше контекста здесь ..
Итак, мой вариант использования для этого - использовать интроспекцию для сигнатур функций (типы аргументов / значения по умолчанию, тип возвращаемого значения, строка документации), чтобы автоматически генерировать для них схему GraphQL (таким образом уменьшая количество шаблонов).
Я все еще немного волнуюсь, будет ли этохорошая идея или нет.
Мне она нравится с точки зрения удобства использования (нет необходимости изучать еще один способ объявления сигнатуры вашей функции: просто аннотируйте ваши типы обычным способом);посмотрите два примера кода, чтобы понять, что я имею в виду: https://github.com/rshk/pyql
Интересно, добавляет ли поддержка универсальных типов (списков, диктов, объединений, ...) с использованием типов из typing
слишком много "черная магия », которая может сломаться неожиданным образом.(На данный момент это не большая проблема, но как насчет будущих версий Python после 3.7? Это станет кошмаром обслуживания?).
Конечно, альтернативой может быть просто использование пользовательской аннотации типа, котораяподдерживает более надежную проверку на будущее, например: https://github.com/rshk/pyql/blob/master/pyql/schema/types/core.py#L337-L339
.. но с другой стороны, это заставит людей помнить, что они должны использовать аннотацию пользовательского типа.Более того, я не уверен, как mypy справится с этим (я предполагаю, что где-то должно быть объявление, чтобы сказать, что пользовательский тип полностью совместим с typing.List
..? По-прежнему звучит хакерски).
(Я в основном спрашиваю предложения по двум подходам, и что наиболее важно, любые плюсы / минусы двух альтернатив, которые я мог бы пропустить. Надеюсь, это не станет «слишком широким» для SO ..).