提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
用Python来处理JSON数据的场景非常多,一般是加载完JSON数据后,通过字典的方式直接去访问JSON字段。如果只是临时性的几行代码,用完就扔,那也没什么,怎么简单怎么来。但如果是需要维护的代码,或者多人协作的代码,那代码还是得写得优雅一点,毕竟team["members"][0]["age"]
这样的代码没有team.members[0].age
读起来那么优雅,而且没有错误检查,更别说后者还有类型提示这样的好处了。
我们来分别看看这两种处理方式,和适用的场景。
一、通过字典的方式访问JSON数据
假设有以下JSON数据要处理:
文件名:team.json
{
"name": "村BA",
"members": [
{
"name": "张三",
"age": 20
},
{
"name": "李四",
"age": 19
},
]
}
Python加载JSON数据:
import json
team = json.loads(open("team.json").read())
加载完返回字典类型team
,然后通过team["name"]
,team["members"][0]["age"]
这样的方式去访问JSON字段。
这就是传统做法,简单快速,但因为没有类型信息,代码编辑器没办法辅助提示,也不会检查错误(例如,team["members"]
写成了team["member"]
),换言之,错误可能要到运行时才会被发现。另外,代码也不够优雅。
这也是Python在v3.5之前存在的问题,直到3.5开始引入类型信息(借鉴了Typescript
)。
二、通过数据类的方式访问JSON数据
相较之下,如果通过数据类的方式去处理,则可以获得以上的种种好处,类型提示,减少运行时错误,更优雅的代码风格。我们来看看怎么做。
2.1 定义数据类
用@dataclass
装饰器来装饰我们的数据类:
from dataclasses import dataclass
from typing import List
@dataclass
class People:
name: str
age: int
@dataclass
class Team:
name: str
members: List[People]
2.2 加载数据
像之前一样,加载JSON数据:
team: Team = json.loads(open('team.json').read())
这时如果访问team.name
会提示'dict' object has no attribute 'members'
错误,尽管我们为team
标注了类型信息Team
,但说到底team
还是一个dict
数据,Python并不会根据标注的类型强制转换,这些手动标注的类型信息是面向用户和代码编辑器,不是面向运行时的。这一点跟Typescript
是一样的,在生成Javascript
代码后Typescript
的类型信息就没了,不会带到运行时。
解决的方案是在加载的时候增加一个object_hook
:
team: Team = json.loads(open('team.json').read(), object_hook=lambda d: SimpleNamespace(**d))
这个解决方案跟@dataclass
本身并没有关系,换言之,不用@dataclass
装饰数据类也可以,但@dataclass
有一些好处:
@dataclass
用于自动为类生成特殊方法,例如 init()、repr()、eq() 等。这使得创建数据类(通常用于存储数据的类)变得更加简单和直观。
使用 @dataclass 装饰器的主要优点包括:
减少样板代码:它自动生成初始化方法、字符串表示方法和相等性比较方法,减少了手动编写这些方法的需要。
可读性:自动生成的方法遵循一致的命名和行为模式,使得代码更加易读和易于维护。
简洁性:使用 @dataclass 可以使代码更加简洁,减少重复代码。
例如:可以直接定义一个对象:person = People("王五", 21)
如果没有
@dataclass
定义,那就必须为People定义一个初始化函数
:def __init__(self, name, age): self.name = name self.age = age
现在代码编辑器就可以给你类型辅助信息了,有错误也会提示(例如,team.members
写成了team.member
立刻报错)。需要维护的代码或多人协助的项目,前期多做一些工作,可以大大减少运行时的错误和维护成本。
总结
两种方法各有利弊,取决于要处理的场景。传统的方式简单快速,适合用完即扔。数据类的方式可以在编写代码时获取编辑器提示,减少运行时错误,但要先对数据进行类型标注,适合需要长期维护和多人协作的场景。