检查混合类型的类型
Check typing for hybrid types
有没有类似于isinstance
的函数,可以识别某些数据是否为混合类型,例如(假设这样的函数命名为isinstance2
):
data = [1, 2, 3]
data2 = {'a', 'b', 'c'}
isinstance2(data, list[int]) # True
isinstance2(data2, list[int]) # False
isinstance2(data2, set[str]) # True
用例是 class 在实例化过程中检查类型:
class foo():
def __init__(self, data):
if isinstance2(data, list[str]):
# do stuff
else:
raise TypeError
如果你想做这种运行类型的检查,你必须明确。 Python 本身没有任何内置内容(尽管可以并且可能确实存在各种库来简化此操作):
class foo():
def __init__(self, data):
if isinstance(data, list) and all(isintance(x, str) for x in data):
# do stuff
else:
raise TypeError
如果你想推广这种类型检查,你可以做类似下面的事情:
from typing import Any, Sequence, Callable, Container
funcs_dict: dict[Any, Callable[[Any, Sequence[Any]], bool]] = {
list: lambda container, args: all(
isinstance(obj, args[0])
for obj in container
),
dict: lambda container, args: all(
isinstance(key, args[0]) and isinstance(value, args[1])
for key, value in container.items()
),
tuple: lambda container, args: all(
isinstance(tuple_elem, arg)
for tuple_elem, arg in zip(container, args)
)
# etc for as many types as you want to include
}
def runtime_generic_type_check(container: Container, annotation: Any) -> bool:
origin: Any = annotation.__origin__
args: Sequence[Any] = annotation.__args__
return (
isinstance(container, origin)
and funcs_dict[origin](container, args)
)
示例:
>>> int_list = [1, 2]
>>> runtime_generic_type_check(int_list, list[int])
True
>>> from typing import List
>>> runtime_generic_type_check(int_list, List[int]
True
>>> runtime_generic_type_check(int_list, list[str]
False
>>> runtime_generic_type_check(int_list, tuple[int])
False
但是,这种方法有一些重要的注意事项。这基本上只适用于非常简单的类型提示——如果你有像 tuple[str, ...]
这样的类型提示,它就不会工作,它也不会适用于像 dict[str, dict[str, int]]
这样的嵌套容器。它也不适用于 Callable[[<parameters>], <return-type>]
等非容器类型提示。您可以更改函数以考虑这些限制,但代码很快就会变得复杂得多。最后,这适用于我的 python 3.9 shell 但不适用于我的 python 3.6 shell — typing.List[str].__origin__
是 typing.List
in python 3.6,而在 python 3.9 中是 list
。我不确定这种变化是什么时候发生的。
有没有类似于isinstance
的函数,可以识别某些数据是否为混合类型,例如(假设这样的函数命名为isinstance2
):
data = [1, 2, 3]
data2 = {'a', 'b', 'c'}
isinstance2(data, list[int]) # True
isinstance2(data2, list[int]) # False
isinstance2(data2, set[str]) # True
用例是 class 在实例化过程中检查类型:
class foo():
def __init__(self, data):
if isinstance2(data, list[str]):
# do stuff
else:
raise TypeError
如果你想做这种运行类型的检查,你必须明确。 Python 本身没有任何内置内容(尽管可以并且可能确实存在各种库来简化此操作):
class foo():
def __init__(self, data):
if isinstance(data, list) and all(isintance(x, str) for x in data):
# do stuff
else:
raise TypeError
如果你想推广这种类型检查,你可以做类似下面的事情:
from typing import Any, Sequence, Callable, Container
funcs_dict: dict[Any, Callable[[Any, Sequence[Any]], bool]] = {
list: lambda container, args: all(
isinstance(obj, args[0])
for obj in container
),
dict: lambda container, args: all(
isinstance(key, args[0]) and isinstance(value, args[1])
for key, value in container.items()
),
tuple: lambda container, args: all(
isinstance(tuple_elem, arg)
for tuple_elem, arg in zip(container, args)
)
# etc for as many types as you want to include
}
def runtime_generic_type_check(container: Container, annotation: Any) -> bool:
origin: Any = annotation.__origin__
args: Sequence[Any] = annotation.__args__
return (
isinstance(container, origin)
and funcs_dict[origin](container, args)
)
示例:
>>> int_list = [1, 2]
>>> runtime_generic_type_check(int_list, list[int])
True
>>> from typing import List
>>> runtime_generic_type_check(int_list, List[int]
True
>>> runtime_generic_type_check(int_list, list[str]
False
>>> runtime_generic_type_check(int_list, tuple[int])
False
但是,这种方法有一些重要的注意事项。这基本上只适用于非常简单的类型提示——如果你有像 tuple[str, ...]
这样的类型提示,它就不会工作,它也不会适用于像 dict[str, dict[str, int]]
这样的嵌套容器。它也不适用于 Callable[[<parameters>], <return-type>]
等非容器类型提示。您可以更改函数以考虑这些限制,但代码很快就会变得复杂得多。最后,这适用于我的 python 3.9 shell 但不适用于我的 python 3.6 shell — typing.List[str].__origin__
是 typing.List
in python 3.6,而在 python 3.9 中是 list
。我不确定这种变化是什么时候发生的。