如何检查值是否与python中的类型匹配? [英] How do I check if a value matches a type in python?

查看:34
本文介绍了如何检查值是否与python中的类型匹配?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个 python 函数,它的单个参数是一个非平凡类型:

从输入导入列表,字典ArgType = List[Dict[str, int]] # 这可以是任何非平凡类型def myfun(a: ArgType) ->没有任何:...

... 然后我有一个从 JSON 源中解压出来的数据结构:

导入json数据 = json.loads(...)

我的问题是:如何在运行时检查data是否具有正确的类型以用作myfun()的参数在将其用作 myfun() 的参数之前?

如果不是 isCorrectType(data, ArgType):raise TypeError("数据类型不正确")别的:我的乐趣(数据)

解决方案

验证类型注释是一项重要的任务.Python 不会自动执行此操作,并且编写自己的验证器很困难,因为 typing 模块没有提供很多有用的界面.(事实上​​,typing 模块的内部结构自从在 python 3.5 中引入以来已经发生了很大的变化,老实说它是一个噩梦.)

这是从我的一个个人项目中获取的类型验证器函数(代码墙警告):

导入检查导入打字__all__ = ['is_instance', 'is_subtype', 'python_type', 'is_generic', 'is_base_generic', 'is_qualified_generic']如果 hasattr(typing, '_GenericAlias'):#蟒蛇3.7def_is_generic(cls):如果是实例(cls,打字._GenericAlias):返回真if isinstance(cls, typing._SpecialForm):返回不在 {typing.Any} 中的 cls返回错误def _is_base_generic(cls):如果是实例(cls,打字._GenericAlias):如果 {typing.Generic, Typing._Protocol} 中的 cls.__origin__:返回错误如果是实例(cls,打字._VariadicGenericAlias):返回真返回 len(cls.__parameters__) >0if isinstance(cls, typing._SpecialForm):在 {'ClassVar', 'Union', 'Optional'} 中返回 cls._name返回错误def_get_base_generic(cls):# Generic 的子类将它们的 _name 设置为 None,但是# 他们的 __origin__ 将指向基础泛型如果 cls._name 是 None:返回 cls.__origin__别的:返回 getattr(打字,cls._name)def_get_python_type(cls):"""像`python_type`,但只适用于`typing` 类."""返回 cls.__origin__def_get_name(cls):返回 cls._name别的:#蟒蛇<3.7如果 hasattr(typing, '_Union'):#蟒蛇3.6def_is_generic(cls):if isinstance(cls, (typing.GenericMeta, Typing._Union, Typing._Optional, Typing._ClassVar)):返回真返回错误def _is_base_generic(cls):if isinstance(cls, (typing.GenericMeta, Typing._Union)):在 {None, ()} 中返回 cls.__args__if isinstance(cls, typing._Optional):返回真返回错误别的:# 蟒蛇 3.5def_is_generic(cls):if isinstance(cls, (typing.GenericMeta, Typing.UnionMeta, Typing.OptionalMeta, Typing.CallableMeta, Typing.TupleMeta)):返回真返回错误def _is_base_generic(cls):if isinstance(cls, typing.GenericMeta):为 cls.__parameters__ 中的 arg 返回所有(isinstance(arg,typing.TypeVar))如果是实例(cls,打字.UnionMeta):返回 cls.__union_params__ 是 Noneif isinstance(cls, typing.TupleMeta):返回 cls.__tuple_params__ 是 Noneif isinstance(cls, typing.CallableMeta):返回 cls.__args__ 是 Noneif isinstance(cls, typing.OptionalMeta):返回真返回错误def_get_base_generic(cls):尝试:返回 cls.__origin__除了属性错误:经过name = type(cls).__name__如果不是 name.endswith('Meta'):raise NotImplementedError("无法确定 {} 的基数".format(cls))名称 = 名称[:-4]返回 getattr(输入,名称)def_get_python_type(cls):"""像`python_type`,但只适用于`typing` 类."""# 许多类实际上从 abc 模块中引用了它们对应的抽象基类# 而不是它们的内置变体(即,typing.List 引用 MutableSequence 而不是列表).# 我们对内置类(如果有)感兴趣,所以我们将遍历 MRO 并在那里寻找它.对于 cls.mro() 中的类型:如果 typ.__module__ == 'builtins' 并且 typ 不是对象:返回类型尝试:返回 cls.__extra__除了属性错误:经过如果 is_qualified_generic(cls):cls = get_base_generic(cls)如果 cls 正在打字.元组:返回元组raise NotImplementedError("无法确定{}的python类型".format(cls))def_get_name(cls):尝试:返回 cls.__name__除了属性错误:返回类型(cls).__name__[1:]如果 hasattr(typing.List, '__args__'):# 蟒蛇 3.6+def_get_subtypes(cls):子类型 = cls.__args__如果 get_base_generic(cls) 正在打字.可调用:如果 len(subtypes) != 2 或 subtypes[0] 不是 ...:子类型 = (子类型[:-1], 子类型[-1])返回子类型别的:# 蟒蛇 3.5def_get_subtypes(cls):if isinstance(cls, typing.CallableMeta):如果 cls.__args__ 是 None:返回 ()返回 cls.__args__, cls.__result__对于 ['__parameters__', '__union_params__', '__tuple_params__'] 中的名称:尝试:子类型 = getattr(cls, name)休息除了属性错误:经过别的:raise NotImplementedError("无法从 {} 中提取子类型".format(cls))subtypes = [typ for typ in subtypes if not isinstance(typ, typing.TypeVar)]返回子类型def is_generic(cls):"""检测任何类型的泛型,例如 `List` 或 `List[int]`.这包括特殊"类型,如Union 和 Tuple - 基本上可以下标的任何东西."""返回_is_generic(cls)def is_base_generic(cls):"""检测通用基类,例如`List`(但不是`List[int]`)"""返回 _is_base_generic(cls)def is_qualified_generic(cls):"""检测带有参数的泛型,例如`List[int]`(但不是`List`)"""返回 is_generic(cls) 而不是 is_base_generic(cls)def get_base_generic(cls):如果不是 is_qualified_generic(cls):raise TypeError('{} 不是合格的 Generic,因此没有 base'.format(cls))返回_get_base_generic(cls)def get_subtypes(cls):返回_get_subtypes(cls)def _instancecheck_iterable(iterable, type_args):如果 len(type_args) != 1:raise TypeError("通用可迭代对象必须正好有 1 个类型参数;找到 {}".format(type_args))type_ = type_args[0]return all(is_instance(val, type_) for val in iterable)def _instancecheck_mapping(mapping, type_args):返回_instancecheck_itemsview(mapping.items(), type_args)def _instancecheck_itemsview(itemsview, type_args):如果 len(type_args) != 2:raise TypeError("通用映射必须恰好有 2 个类型参数;找到 {}".format(type_args))key_type, value_type = type_argsreturn all(is_instance(key, key_type) 和 is_instance(val, value_type) for key, val in itemsview)def _instancecheck_tuple(tup, type_args):如果 len(tup) != len(type_args):返回错误return all(is_instance(val, type_) for val, type_ in zip(tup, type_args))_ORIGIN_TYPE_CHECKERS = {}对于 class_path,在 { 中 check_func# 可迭代对象'typing.Container': _instancecheck_iterable,'typing.Collection': _instancecheck_iterable,'typing.AbstractSet': _instancecheck_iterable,'typing.MutableSet': _instancecheck_iterable,'typing.Sequence': _instancecheck_iterable,'typing.MutableSequence': _instancecheck_iterable,'typing.ByteString': _instancecheck_iterable,'typing.Deque': _instancecheck_iterable,'typing.List': _instancecheck_iterable,'typing.Set': _instancecheck_iterable,'typing.FrozenSet': _instancecheck_iterable,'typing.KeysView': _instancecheck_iterable,'typing.ValuesView': _instancecheck_iterable,'typing.AsyncIterable': _instancecheck_iterable,# 映射'typing.Mapping': _instancecheck_mapping,'typing.MutableMapping': _instancecheck_mapping,'typing.MappingView': _instancecheck_mapping,'typing.ItemsView': _instancecheck_itemsview,'typing.Dict': _instancecheck_mapping,'typing.DefaultDict': _instancecheck_mapping,'typing.Counter': _instancecheck_mapping,'typing.ChainMap': _instancecheck_mapping,# 其他'typing.Tuple': _instancecheck_tuple,}.项目():尝试:cls = eval(class_path)除了属性错误:继续_ORIGIN_TYPE_CHECKERS[cls] = check_funcdef _instancecheck_callable(value, type_):如果不可调用(值):返回错误如果 is_base_generic(type_):返回真param_types, ret_type = get_subtypes(type_)sig = inspect.signature(value)missing_annotations = []如果 param_types 不是 ...:如果 len(param_types) != len(sig.parameters):返回错误# FIXME:添加对 TypeVars 的支持# 如果任何现有注释与类型不匹配,我们将返回 False.# 然后,如果缺少任何注释,我们将抛出异常.对于 param, expected_type in zip(sig.parameters.values(), param_types):param_type = param.annotation如果 param_type 是 inspect.Parameter.empty:missing_annotations.append(param)继续如果不是 is_subtype(param_type, expected_type):返回错误如果 sig.return_annotation 是 inspect.Signature.empty:missing_annotations.append('return')别的:如果不是 is_subtype(sig.return_annotation, ret_type):返回错误如果missing_annotations:raise ValueError("缺少注释:{}".format(missing_annotations))返回真def _instancecheck_union(value, type_):类型 = get_subtypes(type_)返回 any(is_instance(value, typ) for typ in types)def _instancecheck_type(value, type_):# 如果不是类,则返回 False如果不是 isinstance(value, type):返回错误如果 is_base_generic(type_):返回真type_args = get_subtypes(type_)如果 len(type_args) != 1:raise TypeError("Type 必须正好有 1 个类型参数;找到 {}".format(type_args))返回 is_subtype(value, type_args[0])_SPECIAL_INSTANCE_CHECKERS = {'联盟':_instancecheck_union,'Callable': _instancecheck_callable,'类型':_instancecheck_type,'任何':lambda v,t:真,}def is_instance(obj, type_):如果 type_.__module__ == '打字':如果 is_qualified_generic(type_):base_generic = get_base_generic(type_)别的:base_generic = type_名称 = _get_name(base_generic)尝试:验证器 = _SPECIAL_INSTANCE_CHECKERS[名称]除了 KeyError:经过别的:返回验证器(obj,type_)如果 is_base_generic(type_):python_type = _get_python_type(type_)返回 isinstance(obj, python_type)如果 is_qualified_generic(type_):python_type = _get_python_type(type_)如果不是 isinstance(obj, python_type):返回错误base = get_base_generic(type_)尝试:验证器 = _ORIGIN_TYPE_CHECKERS[base]除了 KeyError:raise NotImplementedError("无法对 {} 类型执行 isinstance 检查".format(type_))type_args = get_subtypes(type_)返回验证器(obj,type_args)返回 isinstance(obj, type_)def is_subtype(sub_type, super_type):如果不是 is_generic(sub_type):python_super = python_type(super_type)返回 issubclass(sub_type, python_super)# 此时我们知道 `sub_type` 是一个泛型python_sub = python_type(sub_type)python_super = python_type(super_type)如果不是 issubclass(python_sub, python_super):返回错误# 此时我们知道`sub_type`的基类型是`super_type`的基类型的子类型.# 如果`super_type` 不合格,那么就没有什么可做的了.如果不是 is_generic(super_type) 或 is_base_generic(super_type):返回真# 此时我们知道 `super_type` 是一个合格的泛型......所以如果 `sub_type` 不是# 合格,不能是子类型.如果 is_base_generic(sub_type):返回错误# 此时我们知道这两种类型都是限定泛型,所以我们只需要# 比较它们的子类型.sub_args = get_subtypes(sub_type)super_args = get_subtypes(super_type)返回所有(is_subtype(sub_arg, super_arg) for sub_arg, super_arg in zip(sub_args, super_args))def python_type(注释):"""给定一个类型注解或一个类作为输入,返回相应的 python 类.例子:::>>>python_type(打字.字典)<类'dict'>>>>python_type(typing.List[int])<类'列表'>>>>python_type(int)<类'int'>"""尝试:mro = annotation.mro()除了属性错误:# 如果它没有mro方法,它一定是一个奇怪的打字对象返回_get_python_type(注解)如果输入 mro:返回 annotation.python_typeelif annotation.__module__ == '打字':返回_get_python_type(注解)别的:返回注释

演示:

<预><代码>>>>is_instance([{'x': 3}], List[Dict[str, int]])真的>>>is_instance([{'x': 3}, {'y': 7.5}], List[Dict[str, int]])错误的

(据我所知,这支持所有 python 版本,甚至是使用 的 <3.5typing 模块向后移植.)

Let's say I have a python function whose single argument is a non-trivial type:

from typing import List, Dict
ArgType = List[Dict[str, int]]  # this could be any non-trivial type
def myfun(a: ArgType) -> None:
    ...

... and then I have a data structure that I have unpacked from a JSON source:

import json
data = json.loads(...)

My question is: How can I check at runtime that data has the correct type to be used as an argument to myfun() before using it as an argument for myfun()?

if not isCorrectType(data, ArgType):
    raise TypeError("data is not correct type")
else:
    myfun(data)

解决方案

Validating a type annotation is a non-trivial task. Python does not do it automatically, and writing your own validator is difficult because the typing module doesn't offer much of a useful interface. (In fact the internals of the typing module have changed so much since its introduction in python 3.5 that it's honestly a nightmare to work with.)

Here's a type validator function taken from one of my personal projects (wall of code warning):

import inspect
import typing

__all__ = ['is_instance', 'is_subtype', 'python_type', 'is_generic', 'is_base_generic', 'is_qualified_generic']


if hasattr(typing, '_GenericAlias'):
    # python 3.7
    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


    def _get_base_generic(cls):
        # subclasses of Generic will have their _name set to None, but
        # their __origin__ will point to the base generic
        if cls._name is None:
            return cls.__origin__
        else:
            return getattr(typing, cls._name)


    def _get_python_type(cls):
        """
        Like `python_type`, but only works with `typing` classes.
        """
        return cls.__origin__


    def _get_name(cls):
        return cls._name
else:
    # python <3.7
    if hasattr(typing, '_Union'):
        # python 3.6
        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:
        # python 3.5
        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 _get_base_generic(cls):
        try:
            return cls.__origin__
        except AttributeError:
            pass

        name = type(cls).__name__
        if not name.endswith('Meta'):
            raise NotImplementedError("Cannot determine base of {}".format(cls))

        name = name[:-4]
        return getattr(typing, name)


    def _get_python_type(cls):
        """
        Like `python_type`, but only works with `typing` classes.
        """
        # Many classes actually reference their corresponding abstract base class from the abc module
        # instead of their builtin variant (i.e. typing.List references MutableSequence instead of list).
        # We're interested in the builtin class (if any), so we'll traverse the MRO and look for it there.
        for typ in cls.mro():
            if typ.__module__ == 'builtins' and typ is not object:
                return typ

        try:
            return cls.__extra__
        except AttributeError:
            pass

        if is_qualified_generic(cls):
            cls = get_base_generic(cls)

        if cls is typing.Tuple:
            return tuple

        raise NotImplementedError("Cannot determine python type of {}".format(cls))


    def _get_name(cls):
        try:
            return cls.__name__
        except AttributeError:
            return type(cls).__name__[1:]


if hasattr(typing.List, '__args__'):
    # python 3.6+
    def _get_subtypes(cls):
        subtypes = cls.__args__

        if get_base_generic(cls) is typing.Callable:
            if len(subtypes) != 2 or subtypes[0] is not ...:
                subtypes = (subtypes[:-1], subtypes[-1])

        return subtypes
else:
    # python 3.5
    def _get_subtypes(cls):
        if isinstance(cls, typing.CallableMeta):
            if cls.__args__ is None:
                return ()

            return cls.__args__, cls.__result__

        for name in ['__parameters__', '__union_params__', '__tuple_params__']:
            try:
                subtypes = getattr(cls, name)
                break
            except AttributeError:
                pass
        else:
            raise NotImplementedError("Cannot extract subtypes from {}".format(cls))

        subtypes = [typ for typ in subtypes if not isinstance(typ, typing.TypeVar)]
        return subtypes


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)


def get_base_generic(cls):
    if not is_qualified_generic(cls):
        raise TypeError('{} is not a qualified Generic and thus has no base'.format(cls))

    return _get_base_generic(cls)


def get_subtypes(cls):
    return _get_subtypes(cls)


def _instancecheck_iterable(iterable, type_args):
    if len(type_args) != 1:
        raise TypeError("Generic iterables must have exactly 1 type argument; found {}".format(type_args))

    type_ = type_args[0]
    return all(is_instance(val, type_) for val in iterable)


def _instancecheck_mapping(mapping, type_args):
    return _instancecheck_itemsview(mapping.items(), type_args)


def _instancecheck_itemsview(itemsview, type_args):
    if len(type_args) != 2:
        raise TypeError("Generic mappings must have exactly 2 type arguments; found {}".format(type_args))

    key_type, value_type = type_args
    return all(is_instance(key, key_type) and is_instance(val, value_type) for key, val in itemsview)


def _instancecheck_tuple(tup, type_args):
    if len(tup) != len(type_args):
        return False

    return all(is_instance(val, type_) for val, type_ in zip(tup, type_args))


_ORIGIN_TYPE_CHECKERS = {}
for class_path, check_func in {
                        # iterables
                        'typing.Container': _instancecheck_iterable,
                        'typing.Collection': _instancecheck_iterable,
                        'typing.AbstractSet': _instancecheck_iterable,
                        'typing.MutableSet': _instancecheck_iterable,
                        'typing.Sequence': _instancecheck_iterable,
                        'typing.MutableSequence': _instancecheck_iterable,
                        'typing.ByteString': _instancecheck_iterable,
                        'typing.Deque': _instancecheck_iterable,
                        'typing.List': _instancecheck_iterable,
                        'typing.Set': _instancecheck_iterable,
                        'typing.FrozenSet': _instancecheck_iterable,
                        'typing.KeysView': _instancecheck_iterable,
                        'typing.ValuesView': _instancecheck_iterable,
                        'typing.AsyncIterable': _instancecheck_iterable,

                        # mappings
                        'typing.Mapping': _instancecheck_mapping,
                        'typing.MutableMapping': _instancecheck_mapping,
                        'typing.MappingView': _instancecheck_mapping,
                        'typing.ItemsView': _instancecheck_itemsview,
                        'typing.Dict': _instancecheck_mapping,
                        'typing.DefaultDict': _instancecheck_mapping,
                        'typing.Counter': _instancecheck_mapping,
                        'typing.ChainMap': _instancecheck_mapping,

                        # other
                        'typing.Tuple': _instancecheck_tuple,
                    }.items():
    try:
        cls = eval(class_path)
    except AttributeError:
        continue

    _ORIGIN_TYPE_CHECKERS[cls] = check_func


def _instancecheck_callable(value, type_):
    if not callable(value):
        return False

    if is_base_generic(type_):
        return True

    param_types, ret_type = get_subtypes(type_)
    sig = inspect.signature(value)

    missing_annotations = []

    if param_types is not ...:
        if len(param_types) != len(sig.parameters):
            return False

        # FIXME: add support for TypeVars

        # if any of the existing annotations don't match the type, we'll return False.
        # Then, if any annotations are missing, we'll throw an exception.
        for param, expected_type in zip(sig.parameters.values(), param_types):
            param_type = param.annotation
            if param_type is inspect.Parameter.empty:
                missing_annotations.append(param)
                continue

            if not is_subtype(param_type, expected_type):
                return False

    if sig.return_annotation is inspect.Signature.empty:
        missing_annotations.append('return')
    else:
        if not is_subtype(sig.return_annotation, ret_type):
            return False

    if missing_annotations:
        raise ValueError("Missing annotations: {}".format(missing_annotations))

    return True


def _instancecheck_union(value, type_):
    types = get_subtypes(type_)
    return any(is_instance(value, typ) for typ in types)


def _instancecheck_type(value, type_):
    # if it's not a class, return False
    if not isinstance(value, type):
        return False

    if is_base_generic(type_):
        return True

    type_args = get_subtypes(type_)
    if len(type_args) != 1:
        raise TypeError("Type must have exactly 1 type argument; found {}".format(type_args))

    return is_subtype(value, type_args[0])


_SPECIAL_INSTANCE_CHECKERS = {
    'Union': _instancecheck_union,
    'Callable': _instancecheck_callable,
    'Type': _instancecheck_type,
    'Any': lambda v, t: True,
}


def is_instance(obj, type_):
    if type_.__module__ == 'typing':
        if is_qualified_generic(type_):
            base_generic = get_base_generic(type_)
        else:
            base_generic = type_
        name = _get_name(base_generic)

        try:
            validator = _SPECIAL_INSTANCE_CHECKERS[name]
        except KeyError:
            pass
        else:
            return validator(obj, type_)

    if is_base_generic(type_):
        python_type = _get_python_type(type_)
        return isinstance(obj, python_type)

    if is_qualified_generic(type_):
        python_type = _get_python_type(type_)
        if not isinstance(obj, python_type):
            return False

        base = get_base_generic(type_)
        try:
            validator = _ORIGIN_TYPE_CHECKERS[base]
        except KeyError:
            raise NotImplementedError("Cannot perform isinstance check for type {}".format(type_))

        type_args = get_subtypes(type_)
        return validator(obj, type_args)

    return isinstance(obj, type_)


def is_subtype(sub_type, super_type):
    if not is_generic(sub_type):
        python_super = python_type(super_type)
        return issubclass(sub_type, python_super)

    # at this point we know `sub_type` is a generic
    python_sub = python_type(sub_type)
    python_super = python_type(super_type)
    if not issubclass(python_sub, python_super):
        return False

    # at this point we know that `sub_type`'s base type is a subtype of `super_type`'s base type.
    # If `super_type` isn't qualified, then there's nothing more to do.
    if not is_generic(super_type) or is_base_generic(super_type):
        return True

    # at this point we know that `super_type` is a qualified generic... so if `sub_type` isn't
    # qualified, it can't be a subtype.
    if is_base_generic(sub_type):
        return False

    # at this point we know that both types are qualified generics, so we just have to
    # compare their sub-types.
    sub_args = get_subtypes(sub_type)
    super_args = get_subtypes(super_type)
    return all(is_subtype(sub_arg, super_arg) for sub_arg, super_arg in zip(sub_args, super_args))


def python_type(annotation):
    """
    Given a type annotation or a class as input, returns the corresponding python class.

    Examples:

    ::
        >>> python_type(typing.Dict)
        <class 'dict'>
        >>> python_type(typing.List[int])
        <class 'list'>
        >>> python_type(int)
        <class 'int'>
    """
    try:
        mro = annotation.mro()
    except AttributeError:
        # if it doesn't have an mro method, it must be a weird typing object
        return _get_python_type(annotation)

    if Type in mro:
        return annotation.python_type
    elif annotation.__module__ == 'typing':
        return _get_python_type(annotation)
    else:
        return annotation

Demonstration:

>>> is_instance([{'x': 3}], List[Dict[str, int]])
True
>>> is_instance([{'x': 3}, {'y': 7.5}], List[Dict[str, int]])
False

(As far as I'm aware, this supports all python versions, even the ones <3.5 using the typing module backport.)

这篇关于如何检查值是否与python中的类型匹配?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆