先舉個使用場景:python處理完業務邏輯,將一個結果封裝成統一格式對象,然後json格式返回給客戶端。
本文概要
- python標準類型有哪些?哪些是可變和不可變的,如何轉化?
- obj轉dict有哪些方式?各有啥特點?
- dict又如何轉化爲obj呢?
- 上面的json格式化對象場景,如何實現?
如果上面幾點問題都能回答上,那麼可以跳過本篇文章了。本篇相關文章共三連彈。(端午節在家整理,有幫助記得點個👍,就是對我最大的肯定😘😘😘)
浩瀚的網絡中,你我的相遇也是種緣分,看你天資聰慧、骨骼精奇,就送你一場大造化吧,能領悟多少就看你自己了。㊙傳承之地🙇
1.python類型
標準數據類型
- 不可變
- Number(數字) int、float、bool、complex
- String(字符串)
- Tuple(元組)
- 可變
- List(列表)
- Set(集合)
- Dictionary(字典)
相互轉化
函數 | 描述 |
---|---|
int(x) | 將x轉換爲一個整數 |
float(x) | 將x轉換到一個浮點數 |
complex(real) | 創建一個複數 |
str(x) | 將對象 x 轉換爲字符串 |
repr(x) | 將對象 x 轉換爲表達式字符串 |
eval(str) | 用來計算在字符串中的有效Python表達式,並返回一個對象 |
tuple(s) | 將序列 s 轉換爲一個元組 |
list(s) | 將序列 s 轉換爲一個列表 |
set(s) | 轉換爲可變集合 |
dict(d) | 創建一個字典。d 必須是一個 (key, value)元組序列。 |
frozenset(s) | 轉換爲不可變集合 |
chr(x) | 將一個整數轉換爲一個字符 |
ord(x) | 將一個字符轉換爲它的整數值 |
hex(x) | 將一個整數轉換爲一個十六進制字符串 |
oct(x) | 將一個整數轉換爲一個八進制字符串 |
2.對象轉dict
python的dict只能採用obj["name"]
的方式來寫入和讀取
python的ojb只能採取obj.name
的方式來讀取和寫入
三種方式
__dict__
在
__init__
方法和對象顯示賦值的屬性纔會轉化
- dict函數
需要定義
keys
和__getitem__
方法,所有屬性都會轉化dict
- 取出對象裏屬性名和值,塞到dict中
需要遍歷對象的屬性
三種方式以1千萬的量,測試下它們性能
方式 | 耗時 | 性能 |
---|---|---|
__dict__ |
0.7秒 | 高 |
dict函數 |
11秒 | 中 |
屬性 |
3分12秒 | 低 |
直接取屬性值最快,自定義
__getitem__
方法次之,遍歷屬性最慢
如何對__init__
、__dict__
、vars
、__getitem__
不太瞭解的,可以分別查看下這兩篇文章
2-1.dict
# 對象類
class User:
is_admin = False
desc = ''
def __init__(self, name) -> None:
self.name = name
self.age = 1
# 測試方法
class MyTestCase(unittest.TestCase):
def test_user(self):
user = User('iworkh')
user.is_admin = True
# 只有塞值的時候,纔會轉化爲dic
# desc沒有塞值,dic中沒有
print(user.__dict__)
pass
結果:
{‘name’: ‘iworkh’, ‘age’: 1, ‘is_admin’: True}
name和age
在init函數裏定義,is_admin
通過對象塞值了,三個屬性值都顯示。
而desc
不在init函數中,也沒有塞值,沒有顯示
2-2.dict函數
# 對象類
class Product:
price = 10
desc = ''
def __init__(self, name) -> None:
self.name = name
def keys(self):
'''當對實例化對象使用dict(obj)的時候, 會調用這個方法,這裏定義了字典的鍵, 其對應的值將以obj['name']的形式取,
但是對象是不可以以這種方式取值的, 爲了支持這種取值, 可以爲類增加一個方法'''
return ('name', 'price', 'desc')
def __getitem__(self, item):
'''內置方法, 當使用obj['name']的形式的時候, 將調用這個方法, 這裏返回的結果就是值'''
return getattr(self, item)
# 測試方法
class MyTestCase(unittest.TestCase):
def test_product(self):
product = Product('book')
print(dict(product))
pass
結果:
{‘name’: ‘book’, ‘price’: 10, ‘desc’: ‘’}
都顯示,但是需要定義keys
和__getitem__
函數,上賣弄都有註釋就不解釋了
2-3.屬性方式
工具類
class objDictTool:
@staticmethod
def to_dic(obj):
dic = {}
for fieldkey in dir(obj):
fieldvaule = getattr(obj, fieldkey)
if not fieldkey.startswith("__") and not callable(fieldvaule) and not fieldkey.startswith("_"):
dic[fieldkey] = fieldvaule
return dic
這隻取了不是
__
和_
開頭的屬性,如需要可以修改代碼
# 對象類
class Order:
order_no: str = ''
desc = ''
def __init__(self, name) -> None:
self.name = name
self.age = 1
# 測試方法
class MyTestCase(unittest.TestCase):
def test_order_by_attr(self):
user = User('iworkh')
dic = objDictTool.to_dic(user)
print(dic)
pass
結果:
{‘age’: 1, ‘desc’: ‘’, ‘is_admin’: False, ‘name’: ‘iworkh’}
2-4.總結
三種方式各特點,使用時要注意不同的場合
三種方法以1千萬的量測了下它們性能
方式 | 耗時 | 性能 |
---|---|---|
__dict__ |
0.7秒 | 高 |
dict函數 |
11秒 | 中 |
屬性 |
3分12秒 | 低 |
使用場景:
- 如果您能保證對的屬性值都塞了,或者沒有的值屬性不轉化到dict也沒關係,那麼可以選擇
__dict__
方式 - 如果要obj轉dict的對象不多,整個項目中就幾個類對應的對象,而且要全部屬性(即使沒塞值的,也要轉到dict中),那麼可以選擇
dict函數
方式 - 如果要obj轉dict的類很多,但是不會頻繁轉化(因爲效率低),那麼可以選擇
屬性
方式。
3.dict轉對象
工具類
class objDictTool:
@staticmethod
def to_obj(obj: object, **data):
obj.__dict__.update(data)
通過對象的
__dict__
屬性,然後調用update
更新方法,將data的值賦給obj
class MyTestCase(unittest.TestCase):
def test_dict_to_obj(self):
product_dic = {'name': 'python', 'price': 10, 'desc': 'python對象和dic轉化'}
product_obj = Product('test')
objDictTool.to_obj(product_obj, **product_dic)
print("名稱: {}, 價格: {}, 描述: {}".format(product_obj.name, product_obj.price, product_obj.desc))
pass
結果:
名稱: python, 價格: 10, 描述: python對象和dic轉化
objDictTool裏的一個to_dic
和to_obj
就組成了obj和dict互轉的工具類型了。
4.peewee轉化model
如果項目中使用的了peewee的話,那麼可以使用model_to_dict
和dict_to_model
,將model和dict項目轉化
model定義
class UserModel(BaseModel):
user_name = CharField(max_length=20, null=True, verbose_name="用戶名")
nick_name = CharField(max_length=20, null=True, verbose_name="暱稱")
password = CharField(max_length=20, null=True, verbose_name="密碼")
phone = CharField(max_length=11, index=True, unique=True, verbose_name="手機號碼")
sex = CharField(max_length=1, choices=SEXES, verbose_name="性別")
class Meta:
table_name = 'T_ACTION_USER'
轉化使用
from playhouse.shortcuts import model_to_dict
class UserService:
async def create(self, userModel):
await objects.create(UserModel, **model_to_dict(userModel))
注意:
dict_to_model使用和model_to_dict類似,注意:項目項目使用peewee纔可以用
5.應用
我們來解決,文章開始拋出的使用場景:
python處理完業務邏輯,將一個結果封裝成統一格式對象返回給前臺,以json格式。json格式化需要是python的基本。
比如我們需要每次返回的結果都下:
- 錯誤時:success爲false, errorCode和message有值
- 正常時:success爲true, data有的數據
json格式
{
"success": true,
"errorCode": 0,
"message": "",
"data": {
"id": 2,
"created_date": "2020-06-24T14:47:21",
"update_date": "2020-06-24T14:47:21",
"user_name": "iworkh",
"nick_name": "沐雨雲樓"
}
}
實體類
class Comment:
title: str = ''
author: str = ''
desc: str = ''
create_time: datetime = datetime.now()
5-1.json序列化
開始之前,還是先講得解下json的序列化知識
使用 JSON 函數需要導入 json 庫:import json。
函數 | 描述 |
---|---|
json.dumps | 將 Python 對象編碼成 JSON 字符串 |
json.loads | 將已編碼的 JSON 字符串解碼爲 Python 對象 |
python 原始類型向 json 類型的轉化對照表:
Python | JSON |
---|---|
dict | object |
list, tuple | array |
str, unicode | string |
int, long, float | number |
True | true |
False | false |
None | null |
json 類型轉換到 python 的類型對照表:
JSON | Python |
---|---|
object | dict |
array | list |
string | unicode |
number (int) | int, long |
number (real) | float |
true | True |
false | False |
null | None |
那下面我們來看下如何處理上我們上面的場景
5-2.簡單版
警告
注意下面代碼層層迭代過程,從出錯到正確的演變,最終達到可用代碼。不要只追求結果,要知道其步步演變的過程。
測試類
class MyTestCase(unittest.TestCase):
def test_json(self):
# 返回json時,序列化對象要是python原始類型:
# dict, list, tuple, str, unicode, int, long, float, True, False, None
comment = Comment()
comment.title = 'python工具類-obj轉dict'
comment.author = '沐雨雲樓'
comment.desc = '怎麼玩呢?'
result = {'success': True, 'data': comment}
json_str = json.dumps(result)
print(json_str)
pass
思考有沒問題?能得到預期的結果嘛?
結果:
報錯:
Object of type Comment is not JSON serializable
如果有java基礎肯定直到,一個對象要虛擬化得實現serializable
接口,爲什麼?
可以查看這篇文章 java序列化
解決辦法:
就是本文前面將的如何將obj轉化爲dict的知識了,修改代碼如下
class MyTestCase(unittest.TestCase):
def test_json(self):
# 返回json時,序列化對象要是 python原始類型:
# dict, list, tuple, str, unicode, int, long, float, True, False, None
comment = Comment()
comment.title = 'python工具類-obj轉dict'
comment.author = '沐雨雲樓'
comment.desc = '怎麼玩呢?'
result = {'success': True, 'data': comment}
json_str = json.dumps(result, default=lambda obj: obj.__dict__, sort_keys=True, indent=4, ensure_ascii=False)
print(json_str)
pass
dumps幾個參數說明
default
: 指定如何任意一個對象變成一個可序列爲JSON的對象的函數,這使用的是lambda
表達式sort_keys
: 安裝對象的屬性字母順序排序indent
: json格式化時,使用的空格數ensure_ascii
: False不轉換爲asccii碼
結果
{
"data": {
"author": "沐雨雲樓",
"desc": "怎麼玩呢?",
"title": "python工具類-obj轉dict"
},
"success": true
}
下面的寫法跟上面是完全等效的
class MyTestCase(unittest.TestCase):
def test_json(self):
# 返回json時,序列化對象要是 python原始類型:
# dict, list, tuple, str, unicode, int, long, float, True, False, None
comment = Comment()
comment.title = 'python工具類-obj轉dict'
comment.author = '沐雨雲樓'
comment.desc = '怎麼玩呢?'
result = {'success': True, 'data': comment.__dict__}
json_str = json.dumps(result, sort_keys=True, indent=4, ensure_ascii=False)
print(json_str)
pass
不使用
default
,將data設置爲dict
我們有沒發現,這種方式,如果沒有塞值的話,那麼dict裏是沒有,比如這裏create_time
,json格式化沒出來
塞create_time值
class MyTestCase(unittest.TestCase):
def test_json(self):
# 返回json時,序列化對象要是 python原始類型:
# dict, list, tuple, str, unicode, int, long, float, True, False, None
comment = Comment()
comment.title = 'python工具類-obj轉dict'
comment.author = '沐雨雲樓'
comment.desc = '怎麼玩呢?'
comment.create_time=datetime.now()
result = {'success': True, 'data': comment}
json_str = json.dumps(result, default=lambda obj: obj.__dict__, sort_keys=True, indent=4, ensure_ascii=False)
print(json_str)
pass
這樣create_time
時間就能出來了嘛?
結果:
不好意思,還是沒有。報錯了: ‘datetime.datetime’ object has no attribute ‘dict’
json格式化,要對時間類型特殊處理
文件名json_tool.py的工具類
from datetime import datetime, date
def json_serial(obj):
if isinstance(obj, (datetime, date)):
return obj.isoformat()
raise TypeError("Type {} s not serializable".format(type(obj)))
對對象是datetime和date特殊處理
修改代碼如下:
class MyTestCase(unittest.TestCase):
def test_json(self):
# 返回json時,序列化對象要是 python原始類型:
# dict, list, tuple, str, unicode, int, long, float, True, False, None
comment = Comment()
comment.title = 'python工具類-obj轉dict'
comment.author = '沐雨雲樓'
comment.desc = '怎麼玩呢?'
comment.create_time = datetime.now()
result = {'success': True, 'data': comment.__dict__}
json_str = json.dumps(result, default=json_serial, sort_keys=True, indent=4, ensure_ascii=False)
print(json_str)
pass
這就沒有問題了,最後結果也包含了時間
{
"data": {
"author": "沐雨雲樓",
"create_time": "2020-06-24T22:30:56.890901",
"desc": "怎麼玩呢?",
"title": "python工具類-obj轉dict"
},
"success": true
}
5-3.封裝對象版
我們先上面簡化的缺點,主要以這兩行代碼來說:
簡單版代碼片段
result = {'success': True, 'data': comment.__dict__}
json_str = json.dumps(result, default=json_serial, sort_keys=True, indent=4, ensure_ascii=False)
缺點:
- result是自己定義的一個
{}
dict,每個人可能不一樣,而且可能寫錯,比如success
寫錯了sucesss
,那麼跟前臺接受數據格式對比不上;data
換成了result
等,所有用到的地方都得修改 - json.dumps的參數,每次都要寫一長串
因此,需要對result
進一步封裝,封裝一個對象JsonDataResult
,將對象轉爲json再傳遞。
將result封裝爲JsonDataResult
import json
import 省略
class JsonDataResult:
success: bool = False
errorCode: int = 0
message: str = ''
data: dict = None
def keys(self):
return ('success', 'errorCode', 'message', 'data')
def __getitem__(self, item):
return getattr(self, item)
def to_json(self):
return json.dumps(dict(self), default=json_serial, sort_keys=True, indent=4, ensure_ascii=False)
思考:爲啥這用dict函數
方式來將obj轉爲dict?
因爲: 只有JsonDataResult這一個對象轉化,且返回給前臺都保持數據結構一樣,不管有沒塞值。
使用代碼測試
class MyTestCase(unittest.TestCase):
def test_json_data_result(self):
comment = Comment()
comment.title = 'python工具類-obj轉dict'
comment.author = '沐雨雲樓'
comment.desc = '怎麼玩呢?'
result = JsonDataResult()
result.success = True
result.data = objDictTool.to_dic(comment)
print(result.to_json())
pass
注意幾點:
- 對象都使用
JsonDataResult
封裝的類,不用擔心變量寫錯了,而且大家都一樣對象,返回結構是一樣。 result.to_json
裏的to_json
方法,都調用這個函數,不用每次寫一大段,修改的時候,也只要修改一處即可- 只要保證data的能夠被json轉化即可(data如果是類對象,記得要轉化爲dict類型,具體要看需求,選擇obj轉dict哪一種方式。如項目中使用了peewee的話,那麼可以使用
model_to_dict
)
結果:
{
"data": {
"author": "沐雨雲樓",
"create_time": "2020-06-25T10:17:43.412471",
"desc": "怎麼玩呢?",
"title": "python工具類-obj轉dict"
},
"errorCode": 0,
"message": "",
"success": true
}
5-4.代碼
如需要源碼,可以查看。(關鍵代碼都貼在文章裏,不用看也行)
下面貼出最終版的代碼片段,供閱讀
json_tool.py
from datetime import datetime, date
def json_serial(obj):
if isinstance(obj, (datetime, date)):
return obj.isoformat()
raise TypeError("Type {} s not serializable".format(type(obj)))
obj_dict_tool.py
class objDictTool:
@staticmethod
def to_dic(obj):
dic = {}
for fieldkey in dir(obj):
fieldvaule = getattr(obj, fieldkey)
if not fieldkey.startswith("__") and not callable(fieldvaule) and not fieldkey.startswith("_"):
dic[fieldkey] = fieldvaule
return dic
@staticmethod
def to_obj(obj: object, **data):
obj.__dict__.update(data)
json_data_result.py
import 略
class JsonDataResult:
success: bool = False
errorCode: int = 0
message: str = ''
data: dict = None
def keys(self):
return ('success', 'errorCode', 'message', 'data')
def __getitem__(self, item):
return getattr(self, item)
def to_json(self):
return json.dumps(dict(self), default=json_serial, sort_keys=True, indent=4, ensure_ascii=False)
測試類
class Comment:
title: str = ''
author: str = ''
desc: str = ''
create_time: datetime = datetime.now()
class MyTestCase(unittest.TestCase):
def test_json_data_result(self):
comment = Comment()
comment.title = 'python工具類-obj轉dict'
comment.author = '沐雨雲樓'
comment.desc = '怎麼玩呢?'
result = JsonDataResult()
result.success = True
result.data = objDictTool.to_dic(comment)
print(result.to_json())
pass
6.推薦
能讀到文章最後,首先得謝謝您對本文的肯定,你的肯定是對我們的最大鼓勵。
你覺本文有幫助,那就點個👍
你有疑問,那就留下您的💬
怕把我弄丟了,那就把我⭐
電腦不方便看,那就把發到你📲