Я пытаюсь найти надежный/кросс-версия (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
Я искал "правильный" способ сделать это, но не нашел его в поиске Python docs/google.
Теперь я предполагаю, что должен быть чистый способ сделать эту проверку, так как такие инструменты, как 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..).