目前没有任何官方途径获取这些信息。 typing
模块仍在积极开发中,没有公共 API。(事实上,它可能永远不会有。)我们能做的就是查看模块内部,并找到获取所需信息的最简洁方式。由于该模块仍在开发中,其内部将会有很多变化。
在 Python 3.5 和 3.6 中,泛型有一个 __origin__
属性,其中保存了对原始泛型基类的引用(即,List[int].__origin__
将是 List
),但这在3.7中已更改。现在,最简单的检查某个东西是否为泛型的方法可能是检查其 __parameters__
和 __args__
属性。
以下是一组可用于检测泛型的函数:
import typing
if hasattr(typing, '_GenericAlias'):
def _is_generic(cls):
if isinstance(cls, typing._GenericAlias):
return True
if isinstance(cls, typing._SpecialForm):
return cls not in {typing.Any}
return False
def _is_base_generic(cls):
if isinstance(cls, typing._GenericAlias):
if cls.__origin__ in {typing.Generic, typing._Protocol}:
return False
if isinstance(cls, typing._VariadicGenericAlias):
return True
return len(cls.__parameters__) > 0
if isinstance(cls, typing._SpecialForm):
return cls._name in {'ClassVar', 'Union', 'Optional'}
return False
else:
if hasattr(typing, '_Union'):
def _is_generic(cls):
if isinstance(cls, (typing.GenericMeta, typing._Union, typing._Optional, typing._ClassVar)):
return True
return False
def _is_base_generic(cls):
if isinstance(cls, (typing.GenericMeta, typing._Union)):
return cls.__args__ in {None, ()}
if isinstance(cls, typing._Optional):
return True
return False
else:
def _is_generic(cls):
if isinstance(cls, (typing.GenericMeta, typing.UnionMeta, typing.OptionalMeta, typing.CallableMeta, typing.TupleMeta)):
return True
return False
def _is_base_generic(cls):
if isinstance(cls, typing.GenericMeta):
return all(isinstance(arg, typing.TypeVar) for arg in cls.__parameters__)
if isinstance(cls, typing.UnionMeta):
return cls.__union_params__ is None
if isinstance(cls, typing.TupleMeta):
return cls.__tuple_params__ is None
if isinstance(cls, typing.CallableMeta):
return cls.__args__ is None
if isinstance(cls, typing.OptionalMeta):
return True
return False
def is_generic(cls):
"""
Detects any kind of generic, for example `List` or `List[int]`. This includes "special" types like
Union and Tuple - anything that's subscriptable, basically.
"""
return _is_generic(cls)
def is_base_generic(cls):
"""
Detects generic base classes, for example `List` (but not `List[int]`)
"""
return _is_base_generic(cls)
def is_qualified_generic(cls):
"""
Detects generics with arguments, for example `List[int]` (but not `List`)
"""
return is_generic(cls) and not is_base_generic(cls)
所有这些函数都应该在所有Python版本<=3.7中正常工作(包括任何使用typing
模块后移的版本<3.5)。
typing
,因为它是相当新的。是否有任何关于__args__
的信息?我会继续寻找。 - Jacques Gaudintyping.Tuple[int, str].__origin__
现在是类tuple
,而不是类typing.Tuple
。目前我还没有更好的替代方法 :( 尽管可以进行可行的字符串比较,但...请参见https://bugzilla.redhat.com/show_bug.cgi?id=1598574 (这损坏了Fedora / RHEL安装程序!) - Adam Williamson