Python 如何高效地将 JSON 反序列化为对象 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
mimzy

Python 如何高效地将 JSON 反序列化为对象

  •  
  •   mimzy
    mookrs 2021 年 3 月 22 日 4958 次点击
    这是一个创建于 1860 天前的主题,其中的信息可能已经有所发展或是发生改变。

    现在通过 API 获取的 JSON,我一般先用 HTTPX.json() 方法转换为 Python 内置数据结构,然后用 Pydanticparse_obj_as() 转化为对象(因为 parse_obj_as() 可以很方便地转换 list ),便于使用 type hints 。

    目前的链路是这样的:str -> dict/list -> Pydantic object,有点冗余,而且当 JSON 体积大到一定程度的时候,第二步比第一步慢一个数量级,已经无法接受。所以想了解下,有没有其他高效地将 JSON 反序列化为对象的方法?

    28 条回复    2021-03-23 15:41:59 +08:00
    ungrown
        1
    ungrown  
       2021 年 3 月 22 日
    可以借助 JSONPath 之类的东西做个封装对象
    ch2
        2
    ch2  
       2021 年 3 月 22 日
    自己写个 converter
    hahastudio
        3
    hahastudio  
       2021 年 3 月 22 日   1
    knightdf
        4
    knightdf  
       2021 年 3 月 22 日   1
    ujson,比内置 json 快
    so1n
        5
    so1n  
       2021 年 3 月 22 日
    第一步可以用 ujson ojson 代替 第二步目前还是 pydantic 最快
    mimzy
        6
    mimzy  
    OP
       2021 年 3 月 22 日
    @hahastudio #3 昨天看了 orjson,文档中 https://github.com/ijl/orjson#deserialize 提到 loads() deserializes JSON to Python objects. It deserializes to dict, list, int, float, str, bool, and None objects. 我希望能像 Pydantic 一样返回一个对象而不是内置数据结构,就感觉不太符合…
    abersheeran
        7
    abersheeran  
       2021 年 3 月 22 日   1
    有一个问题,你需要进行默认值的填充和参数校验吗?如果你不需要这些。

    写一个类似于这里面的 https://github.com/abersheeran/index.py/blob/master/indexpy/utils.py#L50 类就行了。基本思路是使用三个魔术方法来自定义 obj.attr 的行为。比起其他需要校验、填充默认值的玩意,快不止一个数量级,因为这里压根就没有 COPY 的损耗。
    no1xsyzy
        8
    no1xsyzy  
       2021 年 3 月 22 日
    那 type hint 就是继承之后再写咯……
    Kobayashi
        9
    Kobayashi &nbp;
       2021 年 3 月 22 日 via Android
    第二步要做类型校验、转换,这能一样吗?
    berserk
        10
    berserk  
       2021 年 3 月 22 日
    import json
    json.loads(s)不行吗
    qlhai
        11
    qlhai  
       2021 年 3 月 22 日
    你都已经用 Python 了,还在乎这点时间吗
    Vegetable
        12
    Vegetable  
       2021 年 3 月 22 日
    parse_obj_as 是要验证的,如果数据是可信的,可以跳过验证,根据官方文档的说法:

    construct() is generally around 30x faster than creating a model with full validation
    mimzy
        13
    mimzy  
    OP
       2021 年 3 月 22 日
    @abersheeran #7 参数校验其实不需要,这个场景下我获取数据,格式基本可以保证。问题在于它传给我的结构嵌套可能比较多,所以希望用自定义对象的方式访问,这样每一层我能知道对象拥有的属性和类型,而不是从字典里一层一层访用 key 访问…你这个方式我学习一下,不过看起来没办法让 IDE 给我提示…
    mimzy
        14
    mimzy  
    OP
       2021 年 3 月 22 日
    @Vegetable #12 是的,昨天也简单看了下 construct(),但是看文档 https://pydantic-docs.helpmanual.io/usage/models/#creating-models-without-validation 它接收的是一堆参数,这就导致我要将返回的 List[Dict[str, Any]] 这样的东西循环一次,然后将 **dict 作为参数,这么构造完已经 30s 了,当然可能我用得不对…

    如果 construct() 和 parse_raw() 能结合的话,我估计效率应该会提升。
    mimzy
        15
    mimzy  
    OP
       2021 年 3 月 22 日
    @berserk #10 .json() 这一步其实就做完了这件事,我想要的是一个我能确定内部结构的对象,类型于 Go 的 structs 。不过这么说的话,突然想起来好像用 TypedDict 注解一下也行…
    Contextualist
        16
    Contextualist  
       2021 年 3 月 22 日   1
    不需要参数校验的话,可以试试 attrs + cattrs 。我自己在用这个方案,但是是用来反序列化配置文件的,所以没有考虑性能。另外 pydantic 我没用过,没有发言权。据 pydantic 作者自己说估计 attrs 能更快: https://github.com/samuelcolvin/pydantic/issues/1459#issuecomment-622045131
    mimzy
        17
    mimzy  
    OP
       2021 年 3 月 22 日
    @mimzy #15 目前的结论:在不需要参数校验的前提下,因为我只是需要掌握对象的内部结构和类型,TypedDict 基本解决了我的需求…
    abersheeran
        18
    abersheeran  
       2021 年 3 月 22 日
    @mimzy 如果你只是需要代码提示,TypedDict 永远的神……https://github.com/abersheeran/baize/blob/master/baize/typing.py#L66
    mimzy
        19
    mimzy  
    OP
       2021 年 3 月 22 日
    @abersheeran #18 我之前就是觉得 TypedDict 只用来做类型注解,然后要写一堆,太浪费了,所以上了 Pydantic 。不过看来有时候返璞归真也挺好…
    abersheeran
        20
    abersheeran  
       2021 年 3 月 22 日
    @mimzy 另外一提,如果你没有用 pydantic 提供的 Cython 编译后的版本,其实它的速度和 attrs 之流差不多甚至慢一些。有时候觉得 pydantic 、fastapi 这些家伙的宣传挺可耻的……虚假宣传
    abersheeran
        21
    abersheeran  
       2021 年 3 月 22 日
    @mimzy 虽然我也是 pydantic 的重度使用者。
    SystemLight
        22
    SystemLight  
       2021 年 3 月 22 日
    我感觉 marshmallow 这个库还挺好用的 https://github.com/marshmallow-code/marshmallow
    Wicked
        23
    Wicked &nbp;
       2021 年 3 月 22 日 via iPhone
    你要多快?找个 rapid json 之类的库自己封装一下?
    mimzy
        24
    mimzy  
    OP
       2021 年 3 月 22 日
    @abersheeran #20 虚假宣传哈哈哈~确实,像我现在用 FastAPI 有时候觉得还挺怀念 Django 的,啥速度不速度的,再快也超不过 Starlette 天花板,现成的工具又少…
    abersheeran
        25
    abersheeran  
       2021 年 3 月 22 日
    @mimzy fastapi 速度还没 aiohttp 快呢。它的性能全靠吹。
    misaka19000
        26
    misaka19000  
       2021 年 3 月 22 日
    我们用的 ujson,用了好几年了,效果挺好
    wangyzj
        27
    wangyzj  
       2021 年 3 月 23 日
    大到一定体积是多大?
    williamfzc
        28
    williamfzc  
       2021 年 3 月 23 日
    一样的问题,我们的需求是 xml -> json -> pydantic object,小文件完全 ok,文件变大之后问题会越来越明显,无论是速度跟内存占用都很爆表。前面的文本处理效率还能接受

    之所以要用 python 主要是一些包调起来方便,现在已经准备慢慢用 java 重写了。。

    @wangyzj 20M 左右就已经慢到不能接受了~
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     899 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 65ms UTC 22:35 PVG 06:35 LAX 15:35 JFK 18:35
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86