V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
推荐学习书目
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
V2EX  ›  Python

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

  •  
  •   mimzy ·
    mookrs · Mar 22, 2021 · 4962 views
    This topic created in 1865 days ago, the information mentioned may be changed or developed.

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

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

    28 replies    2021-03-23 15:41:59 +08:00
    ungrown
        1
    ungrown  
       Mar 22, 2021
    可以借助 JSONPath 之类的东西做个封装对象
    ch2
        2
    ch2  
       Mar 22, 2021
    自己写一个 converter
    hahastudio
        3
    hahastudio  
       Mar 22, 2021   ❤️ 1
    knightdf
        4
    knightdf  
       Mar 22, 2021   ❤️ 1
    ujson,比内置 json 快
    so1n
        5
    so1n  
       Mar 22, 2021
    第一步可以用 ujson ojson 代替 第二步目前还是 pydantic 最快
    mimzy
        6
    mimzy  
    OP
       Mar 22, 2021
    @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  
       Mar 22, 2021   ❤️ 1
    有一个问题,你需要进行默认值的填充和参数校验吗?如果你不需要这些。

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

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

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

    @wangyzj 20M 左右就已经慢到不能接受了~
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2552 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 62ms · UTC 02:10 · PVG 10:10 · LAX 19:10 · JFK 22:10
    ♥ Do have faith in what you're doing.