typing模块(或其他模块)是否有一个API可以在运行时进行类型检查,类似于isinstance(),但能够理解typing中定义的类型类别?
我想要运行类似于以下代码的内容:
from typing import List
assert isinstance([1, 'bob'], List[int]), 'Wrong type'
typing模块(或其他模块)是否有一个API可以在运行时进行类型检查,类似于isinstance(),但能够理解typing中定义的类型类别?
我想要运行类似于以下代码的内容:
from typing import List
assert isinstance([1, 'bob'], List[int]), 'Wrong type'
我正在寻找类似的东西,发现了库typeguard。它可以在任何地方自动执行运行时类型检查。正如问题中直接检查类型一样,这也是受支持的。根据文档,
from typeguard import check_type
# Raises TypeError if there's a problem
check_type('variablename', [1234], List[int])
在 typing
模块中不存在这样的函数,很可能永远都不会有。
检查一个对象是否是某个类的实例——这只意味着“该对象由该类的构造函数创建”——只需要测试一些标记即可。
然而,检查一个对象是否是某种类型的“实例”并不一定是可判定的:
assert isinstance(foo, Callable[[int], str]), 'Wrong type'
虽然检查foo
的类型注释很容易(假设它不是lambda
),但检查它是否符合这些注释通常是不可判定的,这是由于Rice定理。
即使使用更简单的类型,例如List[int]
,测试也很容易变得非常低效,只能用于最小的示例。
xs = set(range(10000))
xs.add("a")
xs.pop()
assert isinstance(xs, Set[int]), 'Wrong type'
类型检查器相对高效的操作技巧是保守:类型检查器试图证明foo
始终返回int
。如果失败,它会拒绝程序,即使该程序可能是有效的,也就是说,这个函数很可能被拒绝,尽管它是完全安全的:
def foo() -> int:
if "a".startswith("a"):
return 1
return "x"
typing
/PEP 484
类型系统是为静态类型检查而构建的,并且不适用于动态类型检查。实际上有用的动态类型系统可以被构建,但它将会非常不同(主要是更简单)与PEP 484
相比。并且可以说,一个相当不错的动态类型系统已经包含在你的现成Python解释器中。 - max我最近发现了一个东西,基本上这个装饰器在运行时进行类型检查,如果某些类型定义不匹配就会引发异常。它还可以对嵌套类型(字符串字典等)进行类型检查。
https://github.com/FelixTheC/strongtyping
示例:
from strongtyping.strong_typing import match_typing
@match_typing
def func_a(a: str, b: int, c: list):
...
func_a('1', 2, [i for i in range(5)])
# >>> True
func_a(1, 2, [i for i in range(5)])
# >>> will raise a TypeMismatch Exception
使用原生的Python,inspect模块可以很容易地解决这个问题 - 不需要外部模块 :)
这有点简单化,确实;它可能无法处理深度嵌套类型(例如需要给定键/值类型的字典),但您可以使用“typing”库来扩展它。
import inspect
def enforce_type_annotation(fn):
parameters = inspect.signature(fn).parameters
param_keys = list(parameters.keys())
def wrapper(*args, **kwargs):
errors = list()
# -- iterate over positionals
for i in range(len(args)):
param = parameters[param_keys[i]]
value = args[i]
# -- if the parameter is not annotated, don't validate.
if not param.annotation:
continue
if not isinstance(value, param.annotation):
errors.append(
f'Positional argument {param} was given type {type(value)} but expected {param.annotation}!'
)
# -- this might throw a KeyError if an incorrect argument is provided
for key, value in kwargs.items():
param = parameters[key]
value = kwargs[key]
# -- if the parameter is not annotated, don't validate.
if not param.annotation:
continue
if not isinstance(value, param.annotation):
errors.append(
f'Keyword argument {param} was given type {type(value)} but expected {param.annotation}!'
)
if len(errors):
raise TypeError('\n'.join(errors))
return fn(*args, **kwargs)
return wrapper
@enforce_type_annotation
def foo(bar: bool, barry: str = None):
return "hello world"
# -- works - keyword arguments remain optional
print(foo(True))
# -- works - all types were passed correctly
print(foo(True, 'Hello'))
# -- does not work, keyword arguments may also be passed as positional
print(foo(True, 1))
# -- does not work, "barry" expects a string
print(foo(True, barry=1))
gitter
与mypy
的开发人员取得联系,似乎类似的功能正在开发中,我会看看能否请项目的某个人在这里回答并在推进过程中进行更新。 - Bertrand Carontyping_inspect
并不完全符合您的需求;它更多地是关于检查类型对象本身。 - Elazar