Python中如何验证对象是否符合 typing.Dict[str, int] 类型?

主要想做的就是

  1. 拿到一个对象, cast 出来这个对象是否符合 typing 里那些奇怪类型, Union Optional 等, 尤其是 Dict[str, int] 时候要判断每一个 key value 的类型
  2. 拿到不符合的类型时候, 自动转换一下, 比如 '1.1' 需要 float 类型, 自动转成 1.1

找过一大些资料都失败了, 目前结论如下

  1. mypy 没法直接看源码, 然后看文档也没找到那种代码里 import mypy 然后做 cast 的功能

  2. typing.cast 在标准库那个是预留给 linter 用的, 实际用不上

  3. 目前已经尝试过 __origin____args__ 来做了, 效果还算不错, 但是感觉自己一点点拆包有点傻, 尤其是自己把不符合的先 _alias, 然后对 _GenericAlias 做上述俩魔术方法, 遇到了 _SpecialForm 直接凉

mypy 官网提到的 MonkeyType (Python 3) and PyAnnotate (type comments only). 还没试


Python中如何验证对象是否符合 typing.Dict[str, int] 类型?

17 回复

1、你是想要做什么呢?如果是想要在每个函数的入口这么干,就太影响程序的性能了。
2、如果是想要对用户的输入(比如 API parameters )进行验证,可以使用 pydantic。

https://pydantic-docs.helpmanual.io/


from typing import Dict, get_type_hints, get_origin, get_args
import inspect

def validate_dict_type(obj, expected_type) -> bool:
    """
    验证对象是否符合指定的 typing.Dict 类型
    
    Args:
        obj: 要验证的对象
        expected_type: 期望的类型,如 Dict[str, int]
    
    Returns:
        bool: 是否符合类型
    """
    # 1. 首先检查是否是字典类型
    if not isinstance(obj, dict):
        return False
    
    # 2. 获取泛型参数
    origin = get_origin(expected_type)
    args = get_args(expected_type)
    
    # 3. 检查是否是 Dict 类型
    if origin is not Dict or len(args) != 2:
        return False
    
    key_type, value_type = args
    
    # 4. 验证所有键值对
    for key, value in obj.items():
        # 验证键类型
        if key_type is not type(None) and not isinstance(key, key_type):
            return False
        # 验证值类型
        if value_type is not type(None) and not isinstance(value, value_type):
            return False
    
    return True

# 使用示例
if __name__ == "__main__":
    # 测试用例
    test_cases = [
        ({"a": 1, "b": 2}, Dict[str, int], True),
        ({1: "a", 2: "b"}, Dict[str, int], False),  # 键不是字符串
        ({"a": "hello", "b": "world"}, Dict[str, int], False),  # 值不是整数
        ({"a": 1, "b": 2.5}, Dict[str, int], False),  # 值包含浮点数
        ([1, 2, 3], Dict[str, int], False),  # 不是字典
        ({}, Dict[str, int], True),  # 空字典
        ({"a": 1, "b": None}, Dict[str, int], False),  # 值包含None
    ]
    
    for obj, expected_type, should_pass in test_cases:
        result = validate_dict_type(obj, expected_type)
        status = "✓" if result == should_pass else "✗"
        print(f"{status} {obj} -> {expected_type}: {result}")

这个实现的核心思路是:先检查对象是否是字典,然后通过get_origin()get_args()提取泛型参数,最后遍历验证每个键值对的类型。注意处理边缘情况如空字典和None类型。

简单来说:用get_origin()get_args()提取类型参数再逐个验证。

pydantic 看过了, 不过当时只看了它的那些 BaseSchema 用法, 就是自定义, 不过我要处理的是 typing 里的内置类型(或者复合类型)
我只是想对某些值做一下类型校验, mypy 上没找到, 官方文档也是说 typing.cast 太影响性能所以什么都不做, 只留给 linters 做猴子补丁
我看看那些 mypy 以外的有没有什么办法吧

好吧, 我傻了, 还是用 pydantic 吧
说白了其实我就是想知道下 typing 里那么多复杂的类型, 怎么做验证, 学习的目的

那是给 IDE 用的

我就是打算研究研究有什么自带的内置方法可以让我验证么, 毕竟 isinstance 是报错的

我先研究研究用 type 动态构造 BaseModel 子类

from pydantic import BaseModel
import typing
import inspect


def test(a: int, b: typing.Dict[str, int]):
pass


sigs = inspect.signature(test)

kwargs = {p.name: p.annotation for p in sigs.parameters.values()}

看了 pydantic 源码,他是这么干的

https://gist.github.com/Trim21/910601a17fbeaa07bd203a93afce6131

我没仔细研究 field.validate 的第二个参数是干啥用的

我之前第一选择是 pydantic , 后来因为看文档不仔细给弃用了, 结果仔细看了下文档, 觉得真香, 把我 3 个多小时写的垃圾全弃用了…

gist 我这边污染打不开, 好容易换 192.30.253.118 结果说 404…

#8


import pydantic.validators
from pydantic.fields import Field

from typing import Dict, Any

field = Field(name=‘d’,
type_=Dict[str, int],
class_validators=None,
model_config=pydantic.BaseConfig)
raw, errs = field.validate({‘key’: ‘value’}, {‘a’: ‘1’}, loc=‘loc’)
print(raw, errs)
# {‘key’: ‘value’}, [<pydantic.error_wrappers.ErrorWrapper object at 0x00000199F1B9BE58>]

感谢,找了半天没找到,vscode 对这些相对路径的跳转太差了。。。已解决

你们思维真是死板啊

这种标准类型直接 jsonschema 校验不就完了 非要纠结到语言的新功能上

感谢提醒, 我都快忘了那个库了, 刚听说 jsonschema 可以支持 Union Optional Dict[str,int]。
呃,show me your code?

只处理过简单的,要__annotations__属性或者 inspect.signature

我上面提到的 __origin__ 和 __args__ 就是…

改用 pydantic 搞定很多类型严重和转换问题.

库是好库, 这两天看他们家源码是真特么看的想吐…

回到顶部