python中實(shí)現(xiàn)json數(shù)據(jù)和類對(duì)象相互轉(zhuǎn)化的四種方式
在日常的軟件測(cè)試過(guò)程中,測(cè)試數(shù)據(jù)的構(gòu)造是一個(gè)占比非常大的活動(dòng)。對(duì)于測(cè)試數(shù)據(jù)的構(gòu)造,分為結(jié)構(gòu)化的數(shù)據(jù)構(gòu)造方式和非結(jié)構(gòu)化的數(shù)據(jù)構(gòu)造方式,反映python代碼里分別是:
- 定義數(shù)據(jù)的class:修改數(shù)據(jù)中的某一個(gè)字段,操作json或者直接針對(duì)class的成員變量進(jìn)行修改。
- 不定義數(shù)據(jù)的class: 直接操作json(dict),來(lái)構(gòu)造或者修改數(shù)據(jù)。
兩種方式各有優(yōu)缺點(diǎn), 對(duì)于業(yè)務(wù)上有明確業(yè)務(wù)含義的數(shù)據(jù),比如請(qǐng)求數(shù)據(jù),建議使用第一種方式,對(duì)數(shù)據(jù)進(jìn)行建模和定義; 而臨時(shí)的數(shù)據(jù)構(gòu)造,可以使用第二種方式,不需要額外定義一些結(jié)構(gòu),成本會(huì)低一些。在使用于第一種方法時(shí),就會(huì)涉及到python中json數(shù)據(jù)與類對(duì)象的相互轉(zhuǎn)化的。
此篇文章,會(huì)通過(guò)4種方式來(lái)展示json數(shù)據(jù)與python的類對(duì)象相互轉(zhuǎn)化:
python的原生方法
jsonpickle
cattrs和attrs庫(kù)
pydantic庫(kù)
以下的例子,都使用同一個(gè)class,并且使用了嵌套的json,算是一個(gè)稍微復(fù)雜的場(chǎng)景。
class Address():
def __init__(self, street, number):
self.street = street
self.number = number
class User():
def __init__(self, name, address):
self.name = name
self.address = Address()
一、python的原生方法
json to python class的參考:https://stackoverflow.com/a/28352366
python class to json的參考: https://stackoverflow.com/a/10252138
以下為代碼示例:
import json
from json import JSONEncoder
class Address():
def __init__(self, street, number):
self.street = street
self.number = number
class User():
def __init__(self, name, address):
self.name = name
self.address = Address(**address)
class MyEncoder(JSONEncoder):
def default(self, o):
return o.__dict__
if __name__ == '__main__':
js = '''{"name":"Cristian", "address":{"street":"Sesame","number":122}}'''
j = json.loads(js)
print(j)
u = User(**j)
print(json.dumps(u, cls = MyEncoder))
print(json.dumps(u.__dict__))
執(zhí)行代碼后,輸出
{'name': 'Cristian', 'address': {'street': 'Sesame', 'number': 122}}
{"name": "Cristian", "address": {"street": "Sesame", "number": 122}}
Traceback (most recent call last):
***********
raise TypeError(f'Object of type {o.__class__.__name__} ‘
TypeError: Object of type Address is not JSON serializable
json轉(zhuǎn)class object時(shí),使用u = User(**j); class object轉(zhuǎn)成json時(shí),對(duì)于一級(jí)的json,直接使用u.__dict__就可以轉(zhuǎn)成json,而對(duì)于嵌套的json,必須使用自定義的JSONEncoder才能轉(zhuǎn)成功。
在以上的轉(zhuǎn)化中,使用了兩個(gè)python的特性,簡(jiǎn)單解釋一下:
- 雙星號(hào)(**) : 在函數(shù)參數(shù)中使用時(shí),用于函數(shù)參數(shù)的解包,使用雙星號(hào)(**)來(lái)解包一個(gè)字典的鍵值對(duì)到一個(gè)函數(shù)的關(guān)鍵字參數(shù)中。
def greet(name, age):
print(f"Hello, my name is {name} and I am {age} years old.")
person = {'name': 'Alice', 'age': 30}
greet(**person) # 等價(jià)于 greet(name='Alice', age=30)
- __dict__屬性: __dict__屬性是一個(gè)內(nèi)置屬性,包含了對(duì)象的屬性及其值,以字典的形式存儲(chǔ)。上面的例子中,使用__dict__屬性,無(wú)法將address的值打印出來(lái),就是因?yàn)橹禐锳ddress對(duì)象,而不是一個(gè)字符串。
優(yōu)點(diǎn):
- 不需要引入其他的三方庫(kù)
缺點(diǎn):
一級(jí)的json數(shù)據(jù)沒(méi)有什么問(wèn)題,多級(jí)的json數(shù)據(jù),to_json轉(zhuǎn)化時(shí),支持的不好,需要自己定義一個(gè)JSONEncoder才能轉(zhuǎn)json成功, 如果更復(fù)雜的json,可能會(huì)失敗。
二、jsonpickle庫(kù)
jsonpickle在github上的介紹如下:
"Python library for serializing any arbitrary object graph into JSON. It can take almost any Python object and turn the object into JSON. Additionally, it can reconstitute the object back into Python.”
這段話,就說(shuō)明了這是一個(gè)專注于class object -> json的庫(kù),而json->object的功能則只支持調(diào)用jsonpickle得到的json,再轉(zhuǎn)回class object.
以下為代碼示例:
import json
import jsonpickle
class Address():
def __init__(self, street, number):
self.street = street
self.number = number
class User():
def __init__(self, name, address):
self.name = name
self.address = Address(**address)
if __name__ == '__main__':
js = '''{"name":"Cristian", "address":{"street":"Sesame","number":122}}'''
j = json.loads(js)
# jsonpickle中沒(méi)有一個(gè)類似jsonpickle.decode(j, class = User)的方法,所以只能拿第一種方法初始化class
u = User(**j)
print(jsonpickle.encode(u, unpicklable=False))
jp = jsonpickle.encode(u)
print(jp)
u2 = jsonpickle.decode(jp)
print(u2.__class__)
執(zhí)行后的輸出為:
{"name": "Cristian", "address": {"street": "Sesame", "number": 122}}
{"py/object": "__main__.User", "name": "Cristian", "address": {"py/object": "__main__.Address", "street": "Sesame", "number": 122}}
<class '__main__.User'>
一些說(shuō)明:
1. jsonpickle中沒(méi)有一個(gè)類似jsonpickle.decode(j, class = User)的方法,所以只能拿第一種方法初始化class
- 轉(zhuǎn)成json時(shí), 調(diào)用jsonpickle.encode方法,傳入?yún)?shù)unpicklable=False時(shí),返回值不包含把json數(shù)據(jù)轉(zhuǎn)回python object的信息,得到一個(gè)通用的json字符串
3. 轉(zhuǎn)成json時(shí), 調(diào)用jsonpickle.encode方法,默認(rèn)unpicklable=True時(shí),返回值中包含python object的信息,比如對(duì)象的類,輸出中的"py/object": "_main_.User"就是這些信息
- json轉(zhuǎn)成python object時(shí), 必須使用unpicklable=True時(shí)的json數(shù)據(jù),jsonpickle在json轉(zhuǎn)object時(shí)的局限性就在于此。
優(yōu)點(diǎn):
- object -> json很強(qiáng)大,可以直接使用
缺點(diǎn):
- json轉(zhuǎn)object時(shí), 比較雞肋,基本不能直接使用
三、cattrs和attrs庫(kù)
attrs庫(kù): https://github.com/python-attrs/attrs。 attrs可以簡(jiǎn)化類的定義的管理,使用后這些類將自動(dòng)獲得一些有用的特性,如初始化方法(_init_)、__repr__方法、__eq__和__hash__等。實(shí)際使用的話,最基礎(chǔ)的只需要知道attr.s和attr.ib兩個(gè)方法即可。
cattrs庫(kù): https://github.com/python-attrs/cattrs。cattrs(即“conversion attrs”)是一個(gè)與attrs緊密集成的庫(kù),它提供了對(duì)象到字典(或其他數(shù)據(jù)結(jié)構(gòu))的序列化和從字典(或其他數(shù)據(jù)結(jié)構(gòu))到對(duì)象的反序列化功能。
以下為代碼:
import json
import attr
import cattrs
@attr.s
class Address:
street = attr.ib(type = str)
number = attr.ib(type = int)
@attr.s
class User:
name = attr.ib(type = str)
address = attr.ib(type=Address) # adrress 為Addres類型
if __name__ == "__main__":
js = '''{"name":"Cristian", "address":{"street":"Sesame","number":122}}'''
j = json.loads(js)
u = cattrs.structure(j, User)
print(u, u.__class__)
print(cattrs.unstructure(u))
執(zhí)行后,輸出:
User(name='Cristian', address=Address(street='Sesame', number=122), <class '__main__.User'>)
{'name': 'Cristian', 'address': {'street': 'Sesame', 'number': 122}}
一些說(shuō)明:
- attr.s是一個(gè)裝飾器,用來(lái)標(biāo)記類為attrs類。
- attr.ib是一個(gè)用于聲明屬性的工廠函數(shù), 比如attr.ib(type=str)表示一個(gè)類型為str的屬性。在類定義中,使用attr.s和attr.ib基本就夠了,在attr.ib方法中,還有很多參數(shù),比如默認(rèn)值、validator等,可以用于檢查成員是不是滿足定義的屬性。
- cattrs.structure和cattrs.unstructure用來(lái)將attrs類和json dict之間的相互轉(zhuǎn)化,含義非常的直觀。
優(yōu)點(diǎn):
- 使用cattrs和attrs結(jié)合使用,在與json的轉(zhuǎn)化中,非常簡(jiǎn)單和強(qiáng)大,而且結(jié)構(gòu)化的數(shù)據(jù)定義非常直觀。
四、pydantic庫(kù)
Pydantic庫(kù): https://github.com/pydantic/pydantic。數(shù)據(jù)驗(yàn)證和解析的Python庫(kù),提供類型注解、數(shù)據(jù)驗(yàn)證和模型轉(zhuǎn)換功能。使用Pydantic可以定義模型類,驗(yàn)證輸入數(shù)據(jù)并轉(zhuǎn)換為字典或JSON。
直接上代碼:
import json
from pydantic import BaseModel, Field
class Address(BaseModel):
street: str #pydantic使用類型注解, 來(lái)確保使用正確的類型提示來(lái)定義字段
number: int
class User(BaseModel):
name: str
address: Address
if __name__ == "__main__":
js = '''{"name":"Cristian", "address":{"street":"Sesame","number":122}}'''
j = json.loads(js)
u = User.parse_obj(j)
print(u, u.__class__)
print(u.json())
執(zhí)行代碼后,輸出:
name='Cristian' address=Address(street='Sesame', number=122) <class '__main__.User'>
{"name": "Cristian", "address": {"street": "Sesame", "number": 122}}
一些說(shuō)明:
- 通過(guò)繼承pydantic的BaseModel來(lái)定義類,類型注解來(lái)定義字段的類型,創(chuàng)建符合需求的數(shù)據(jù)模型(這里也能看出數(shù)據(jù)模型是pydantic的核心)。
- 繼承BaseModel,直接使用parse_obj方法就可以將json數(shù)據(jù)轉(zhuǎn)為class object, 直接使用json()方法就可以轉(zhuǎn)為json數(shù)據(jù)。
優(yōu)點(diǎn):
- 在與json的轉(zhuǎn)化中,非常簡(jiǎn)單和強(qiáng)大,直接調(diào)用結(jié)構(gòu)體的方法就可,而且結(jié)構(gòu)化的數(shù)據(jù)定義非常直觀
總結(jié):
針對(duì)python中類對(duì)象和json的相關(guān)轉(zhuǎn)化問(wèn)題, 本文介紹了4種方式,涉及了三個(gè)非常強(qiáng)大的python庫(kù)jsonpickle、attrs和cattrs、pydantic,但是這些庫(kù)的功能并未涉及太深。在工作中,遇到實(shí)際的問(wèn)題時(shí),可以根據(jù)這幾種方法,靈活選取。
再回到結(jié)構(gòu)化測(cè)試數(shù)據(jù)的構(gòu)造,當(dāng)需要對(duì)數(shù)據(jù)進(jìn)行建模時(shí),也就是賦予數(shù)據(jù)業(yè)務(wù)含義,pydantic應(yīng)該是首選,目前(2024.7.1)來(lái)看,pydantic的生態(tài)非?;钴S,各種基于pydantic的工具也非常多,建議嘗試。
以上就是python中實(shí)現(xiàn)json數(shù)據(jù)和類對(duì)象相互轉(zhuǎn)化的四種方式的詳細(xì)內(nèi)容,更多關(guān)于python json和類對(duì)象相互轉(zhuǎn)化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python雙精度浮點(diǎn)數(shù)運(yùn)算并分行顯示操作示例
這篇文章主要介紹了Python雙精度浮點(diǎn)數(shù)運(yùn)算并分行顯示操作,涉及Python數(shù)學(xué)運(yùn)算及顯示相關(guān)操作技巧,注釋備有詳盡的說(shuō)明,需要的朋友可以參考下2017-07-07
pytorch?cuda安裝報(bào)錯(cuò)的解決方法
這篇文章主要給大家介紹了關(guān)于pytorch?cuda安裝報(bào)錯(cuò)的解決方法,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Pytorch具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2023-01-01
Python的Flask開(kāi)發(fā)框架簡(jiǎn)單上手筆記
這篇文章主要介紹了Python的Flask開(kāi)發(fā)框架的入門知識(shí)整理,Flask是一款極輕的Python web開(kāi)發(fā)框架,需要的朋友可以參考下2015-11-11
PyTorch中torch.utils.data.Dataset的介紹與實(shí)戰(zhàn)
PyTorch是一個(gè)開(kāi)源的Python機(jī)器學(xué)習(xí)庫(kù),基于Torch,用于自然語(yǔ)言處理等應(yīng)用程序,下面這篇文章主要給大家介紹了關(guān)于PyTorch中torch.utils.data.Dataset的介紹與實(shí)戰(zhàn),需要的朋友可以參考下2022-06-06
python爬蟲(chóng) 使用真實(shí)瀏覽器打開(kāi)網(wǎng)頁(yè)的兩種方法總結(jié)
下面小編就為大家分享一篇python爬蟲(chóng) 使用真實(shí)瀏覽器打開(kāi)網(wǎng)頁(yè)的兩種方法總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-04-04

