什麼是序列化,把程序中的對象或者變量,從內存中轉換爲可存儲或可傳輸的過程稱爲序列化。在 Python 中,這個過程稱爲 pickling,在其餘語言中也被稱爲 serialization,marshalling,flattening 等。程序中的對象(或者變量)在序列化以後,就能夠直接存放到存儲設備上,或者直接發送到網絡上進行傳輸。python
序列化的逆向過程,即爲反序列化(unpickling),就是把序列化的對象(或者變量)從新讀到內存中~編程
json 模塊就用於序列化和反序列化。對象(變量)使用json模塊序列化後,表現爲一個字符串,序列化爲字符串格式的好處是:序列化後的對象能夠在不一樣的編程語言之間傳遞。
python 數據類型和 json 中的字符串對應關係以下:json
python數據類型 | json字符串 |
---|---|
dict | '{}' |
list | '[]' |
tuple | '[]' |
str | 'string' |
int/float | '1.23' |
True/False | true/false |
None | null |
json模塊經常使用的就4個方法:dump,dumps,load,loads~網絡
這兩個方法用於序列化對象,兩個方法的功能相似,區別在於,json.dumps 方法接收一個參數,即須要序列化的對象,其他參數爲可選參數,方法執行完成後,會返回序列化後獲得的字符串;json.dump 接收兩個參數,第一個參數和 dumps方法 相同,即須要序列化的對象,第二個參數爲文件對象,例如 open方法 的返回對象,其他爲可選參數,方法執行後,序列化後的字符串會直接寫到文件中~
dump / dumps 示例:app
import json d = {'name': '貝貝', 'age': 18} lst = [1, 2, 3] tup = ('a', 'b', 'c') s = 'hello' i = 3 f = 1.2 flag_1 = True flag_2 = False abc = None print(type(json.dumps(d))) # <class 'str'> print(json.dumps(d)) # {"name": "\u8d1d\u8d1d", "age": 18} print(json.dumps(lst)) # [1, 2, 3] print(json.dumps(tup)) # ["a", "b", "c"] print(json.dumps(s)) # "hello" print(json.dumps(i)) # 3 print(json.dumps(f)) # 1.2 print(json.dumps(flag_1)) # true print(json.dumps(flag_2)) # false print(json.dumps(abc)) # null # 以上的輸出類型都是 class 'str' 類型,即字符串類型~ ################################# d = {'name': '貝貝', 'age': 18} with open(file='/tmp/test_json', mode='w') as f: json.dump(d, f) # 執行完成後,查看輸出的文件內容: ➜ ~ cat /tmp/test_json {"name": "\u8d1d\u8d1d", "age": 18}%
這兩個方法用於序列化後的字符串 反序列化,二者的區別和 dump、dumps 相似,json.loads 接收一個字符串參數,其他參數爲可選參數,json.load 也接收一個參數,該參數爲包含 json 字符串的文件對象~編程語言
json.loads示例:ide
d = json.loads('{"name": "\u8d1d\u8d1d", "age": 18}') print(type(d), '--', d) abc = json.loads('null') print(type(abc), '--', abc) tup = json.loads('["a", "b", "c"]') print(type(tup), '--', tup) s = json.loads('"hello"') print(type(s), '--', s) # 輸出結果: <class 'dict'> -- {'name': '貝貝', 'age': 18} <class 'NoneType'> -- None <class 'list'> -- ['a', 'b', 'c'] <class 'str'> -- hello
注意:傳遞給 json.loads 方法的參數必須用 單引號括起來,裏面的字符串使用雙引號,例如不能有這樣的寫法:json.loads("hello"),json.loads("['a', 'b', 'c']"),json 字符串中不支持單引號~
json.load示例:函數
with open(file='/tmp/test_json', mode='r') as f: json_data = json.load(f) print(type(json_data), '--', json_data) # 輸出結果: <class 'dict'> -- {'name': '貝貝', 'age': 18}
咱們能夠看到上述示例中,字典對象中包含有中文字符,在進行序列化後,無論是使用 dumps 存放到字符串中 仍是使用 dump 存放到文件中,中文字符串是使用 unicode 編碼格式存放的。
在Python3中,代碼中的字符串都是使用 unicode 格式存放的,序列化以後也是以unicode 格式存放,因此序列化和反序列化過程都不存在問題。編碼
Python2中,代碼中的字符串是 str類型,str類型 和 unicode類型 的關係以下所示:code
unicode -----> encode --------> str(例如爲 utf-8編碼) utf-8(例如爲 utf-8編碼) --------> decode ----------> unicode
因此在Python2中,序列化過程和反序列化過程都有涉及到轉碼過程(encode和decode),序列化過程 會先將對象中的字符串 使用utf-8 進行解碼(decode),轉換爲unicode類型後,再存放到文件或者字符串中,反序列化過程 會將 json字符串 使用utf-8 編碼(encode),而後存放到內存中的變量~
說明:在Python2中,dumps(dump)和loads(load)默認使用 utf-8 進行 encode和decode,若要使用使用其餘編碼方式,能夠經過 encode參數 指定;在Python3中,dumps(dump)和loads(load)方法都沒有 encode參數~
來看以下示例:
# -*- coding:utf-8 -*- d = {'name': '貝貝', 'age': 18} print type(json.dumps(d)), '--', json.dumps(d) res_d = json.loads('{"age": 18, "name": "\u8d1d\u8d1d"}') print type(res_d), '--', res_d # 結果輸出: <type 'str'> -- {"age": 18, "name": "\u8d1d\u8d1d"} <type 'dict'> -- {u'age': 18, u'name': u'\u8d1d\u8d1d'}
如上過程,序列化和反序列化都沒有問題,這是由於,文件的開頭指定了 ‘# -- coding:utf-8 --’,程序中的字符串(str類型)就是使用utf-8編碼後存放於內存中~
如今修改一下文件開頭的編碼:
# -*- coding:gbk -*- d = {'name': '貝貝', 'age': 18} print type(json.dumps(d)), '--', json.dumps(d) res_d = json.loads('{"age": 18, "name": "\u8d1d\u8d1d"}') print type(res_d), '--', res_d
這個時候就會報出以下錯誤信息,很簡單,utf-8 沒法解碼 gbk編碼的字符串('貝貝')
UnicodeDecodeError: 'utf8' codec can't decode byte 0xb1 in position 0: invalid start byte
解決方法就是,在 dumps 過程當中指定使用 gbk 進行解碼,而後輸出就正常了~
# -*- coding:gbk -*- d = {'name': '貝貝', 'age': 18} json_str = json.dumps(d, encoding='gbk') print type(json_str), '--', json_str res_d = json.loads('{"age": 18, "name": "\u8d1d\u8d1d"}') print type(res_d), '--', res_d # 輸出結果: <type 'str'> -- {"age": 18, "name": "\u8d1d\u8d1d"} <type 'dict'> -- {u'age': 18, u'name': u'\u8d1d\u8d1d'}
注意:這裏 loads 過程不須要指定編碼格式,由於反序列化以後存放到內存中的依舊是unicode格式的字符串~
還有一種更簡單的解決方式,即在中文字符前加 u
d = {'name': u'貝貝', 'age': 18} json_str = json.dumps(d)
pickle 模塊也用於序列化和反序列化Python對象(變量),其用法和 json 模塊的使用基本一致。pickle 模塊 和 json 模塊 區別在於:pickle 模塊 僅用於Python的數據類型,序列化後的對象不能再不一樣的編程語言之間傳遞,可是 pickle 模塊 可序列化幾乎全部的Python數據類型,包括時間對象,函數,類…
import pickle d = {'name': '貝貝', 'age': 18} d_dump = pickle.dumps(d) print(d_dump) d_load = pickle.loads(d_dump) print(type(d_load), '--', d_load) # 結果輸出: b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x06\x00\x00\x00\xe8\xb4\x9d\xe8\xb4\x9dq\x02X\x03\x00\x00\x00ageq\x03K\x12u.' # 注意 輸出的是 byte 類型(即python2中的str類型) <class 'dict'> -- {'name': '貝貝', 'age': 18} #################################### lst = [1, 2, 3] with open('/tmp/test_pickle', mode='wb') as f: # 打開文件的模式爲二進制寫 pickle.dump(lst, f) with open('/tmp/test_pickle', mode='rb') as f: # 打開文件的模式爲二進制讀 lst_load = pickle.load(f) print(type(lst_load), '--', lst_load) # 結果輸出: <class 'list'> -- [1, 2, 3] # 存放序列化對象的文件: ➜ ~ cat /tmp/test_pickle q(KKKe.%
能夠看到 pickle序列化後的數據,可讀性差,不像json那樣一目瞭然~
import pickle class Person: def __init__(self, name, age): self.name = name self.age = age def say_hello(self): print('hello %s' % (self.name)) p = Person('貝貝', 18) # p.say_hello() # del Person with open('/tmp/test_pickle', mode='wb') as f: pickle.dump(p, f) with open('/tmp/test_pickle', mode='rb') as f: p_load = pickle.load(f) p_load.say_hello() # 輸出結果: hello 貝貝
注意:中途不能del Person,否則會出現以下錯誤
_pickle.PicklingError: Can't pickle <class '__main__.Person'>: attribute lookup Person on __main__ failed
shelve 模塊也用於序列化,shelve 模塊是在 pickle 模塊上作了一層封裝,也僅支持兩個Python程序之間進行交換~,優勢是 shelve 模塊 能夠序列化 Python 的全部數據類型~
shelve 模塊比 pickle 模塊更加簡單,只有一個 open函數,返回相似字典的對象,可讀可寫,當爲某個 key 賦值時,這個值會被序列化,並進行存儲;經過某個 key 讀出對應的值時,便是一個反序列化過程,其中 key 必須爲字符串,而值能夠是python所支持的數據類型。
shelve 模塊存取過程:
import shelve class Person: def __init__(self, name, age): self.name = name self.age = age def say_hello(self): print('hello %s' % (self.name)) p = Person('貝貝', 18) d = {'name': 'abc', 'age': 20} f = shelve.open(r'/tmp/test_shelve') f['d_info'] = d f['p_info'] = p print(f.get('d_info')) f.get('p_info').say_hello() f.close()
如上過程,class對象和基本數據類型會被序列化並存放在文件 '/tmp/test_shelve' 中,f.get() 取出過程便是一個反序列化過程~
如果一個可變對象,使用 shelve 模塊序列化以後存放到文件中,而後取出(get)對可變對象進行更改,這個時候,已經改變的可變對象只是保存在內存中,不會被寫入到文件中,看以下示例:
import shelve f = shelve.open(r'/tmp/test_shelve') f['lst_info'] = [1,2,3] f.get('lst_info').append(4) print(f.get('lst_info')) # 輸出結果: [1, 2, 3]
若要進行更改須要從新寫入,即從新序列化:
import shelve f = shelve.open(r'/tmp/test_shelve') f['lst_info'] = [1, 2, 3] lst = f.get('lst_info') lst.append(4) f['lst_info'] = lst print(f.get('lst_info')) # 輸出結果: [1, 2, 3, 4]
或者在使用 shelve 打開文件時,設置 writeback 爲True:
f = shelve.open(r'/tmp/test_shelve', writeback=True) f['lst_info'] = [1, 2, 3] f.get('lst_info').append(4) print(f.get('lst_info')) # 輸出結果: [1, 2, 3, 4]
.................^_^