python中序列化對(duì)象的方法主要有pickle和json,針對(duì)安全性、性能、兼容性和可讀性進(jìn)行選擇。1.pickle模塊支持復(fù)雜對(duì)象和自定義類的序列化,但存在安全風(fēng)險(xiǎn),尤其在反序列化不可信數(shù)據(jù)時(shí);2.json模塊更安全,但僅限于內(nèi)置數(shù)據(jù)類型的序列化,適用于跨語言兼容和文本可讀性要求高的場景;3.處理循環(huán)引用時(shí),pickle默認(rèn)跟蹤對(duì)象避免重復(fù)序列化,但復(fù)雜情況下建議手動(dòng)打破循環(huán),如將引用設(shè)為none后再恢復(fù);4.自定義序列化可通過__getstate__和__setstate__方法實(shí)現(xiàn),靈活控制屬性的保存與恢復(fù)過程。綜合考慮需求后合理選用方法,若僅python內(nèi)部使用且追求性能可選pickle,否則優(yōu)先用json。
Python中序列化對(duì)象,簡單說就是把對(duì)象變成字符串或者字節(jié)流,方便存儲(chǔ)或傳輸。避免循環(huán)引用,得用點(diǎn)技巧,不然會(huì)卡死。
解決方案
Python提供了pickle模塊來做序列化,但pickle容易出安全問題,特別是反序列化不可信數(shù)據(jù)的時(shí)候。更安全的選擇是json,但json只能序列化Python內(nèi)置的一些類型,比如字典、列表、字符串、數(shù)字等。如果你的對(duì)象比較復(fù)雜,或者自定義的類,json就搞不定了。
pickle的基本用法:
立即學(xué)習(xí)“Python免費(fèi)學(xué)習(xí)筆記(深入)”;
import pickle class MyClass: def __init__(self, data): self.data = data obj = MyClass(123) # 序列化到文件 with open('my_object.pkl', 'wb') as f: pickle.dump(obj, f) # 從文件反序列化 with open('my_object.pkl', 'rb') as f: loaded_obj = pickle.load(f) print(loaded_obj.data) # 輸出: 123
避免循環(huán)引用,pickle其實(shí)自帶了處理機(jī)制。它會(huì)跟蹤已經(jīng)序列化的對(duì)象,如果遇到重復(fù)的對(duì)象,就不會(huì)再次序列化,而是保存一個(gè)引用。但如果循環(huán)引用過于復(fù)雜,可能會(huì)導(dǎo)致性能問題。
一個(gè)更靠譜的方法是手動(dòng)打破循環(huán)引用。在序列化之前,把循環(huán)引用的地方設(shè)置為None,序列化之后再恢復(fù)。
import pickle class Node: def __init__(self, name): self.name = name self.parent = None self.children = [] def add_child(self, child): self.children.append(child) child.parent = self node1 = Node("Node 1") node2 = Node("Node 2") node3 = Node("Node 3") node1.add_child(node2) node2.add_child(node3) node3.add_child(node1) # 循環(huán)引用 # 序列化前打破循環(huán)引用 node3.parent = None # 序列化 serialized_data = pickle.dumps(node1) # 反序列化 loaded_node1 = pickle.loads(serialized_data) # 恢復(fù)循環(huán)引用(可選,如果需要的話) # loaded_node3 = loaded_node1.children[0].children[0] # loaded_node3.parent = loaded_node1
如何選擇合適的序列化方法?
選擇序列化方法,要考慮幾個(gè)因素:安全性、性能、兼容性和可讀性。
- 安全性: 如果要序列化的數(shù)據(jù)來自不可信的來源,千萬別用pickle。json更安全,但只能序列化簡單的數(shù)據(jù)類型。
- 性能: pickle通常比json快,但如果對(duì)象結(jié)構(gòu)復(fù)雜,json可能更有效率。
- 兼容性: json是一種通用的數(shù)據(jù)格式,在不同的編程語言和平臺(tái)之間都有很好的兼容性。pickle只能在Python中使用。
- 可讀性: json是文本格式,可讀性好。pickle是二進(jìn)制格式,可讀性差。
如果對(duì)性能要求不高,而且數(shù)據(jù)結(jié)構(gòu)簡單,json是首選。如果性能是關(guān)鍵,而且數(shù)據(jù)只在Python中使用,可以考慮pickle,但一定要注意安全問題。
如何自定義序列化和反序列化過程?
有時(shí)候,默認(rèn)的序列化方式可能不滿足需求。比如,你可能想忽略某些屬性,或者對(duì)某些屬性進(jìn)行特殊處理。
pickle提供了__getstate__和__setstate__兩個(gè)方法,可以讓你自定義序列化和反序列化的過程。
- __getstate__:在序列化之前被調(diào)用,返回一個(gè)對(duì)象的狀態(tài)。你可以選擇返回哪些屬性,或者對(duì)屬性進(jìn)行修改。
- __setstate__:在反序列化之后被調(diào)用,接收__getstate__返回的狀態(tài)。你可以用這個(gè)狀態(tài)來恢復(fù)對(duì)象。
import pickle class MyClass: def __init__(self, a, b, c): self.a = a self.b = b self.c = c def __getstate__(self): # 只序列化a和b return {'a': self.a, 'b': self.b} def __setstate__(self, state): # 恢復(fù)a和b,c設(shè)置為默認(rèn)值 self.a = state.get('a', 0) self.b = state.get('b', 0) self.c = -1 # 默認(rèn)值 obj = MyClass(1, 2, 3) # 序列化 serialized_data = pickle.dumps(obj) # 反序列化 loaded_obj = pickle.loads(serialized_data) print(loaded_obj.a, loaded_obj.b, loaded_obj.c) # 輸出: 1 2 -1
這樣,你就可以靈活地控制對(duì)象的序列化和反序列化過程,滿足各種特殊需求。