国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

腳本之家,腳本語言編程技術(shù)及教程分享平臺!
分類導(dǎo)航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務(wù)器之家 - 腳本之家 - Python - 詳解Python之?dāng)?shù)據(jù)序列化(json、pickle、shelve)

詳解Python之?dāng)?shù)據(jù)序列化(json、pickle、shelve)

2020-09-27 13:17云游道士 Python

本篇文章主要介紹了Python之?dāng)?shù)據(jù)序列化,本節(jié)要介紹的就是Python內(nèi)置的幾個用于進(jìn)行數(shù)據(jù)序列化的模塊,有興趣的可以了解一下。

一、前言

1. 現(xiàn)實需求

每種編程語言都有各自的數(shù)據(jù)類型,其中面向?qū)ο蟮木幊陶Z言還允許開發(fā)者自定義數(shù)據(jù)類型(如:自定義類),Python也是一樣。很多時候我們會有這樣的需求:

  1. 把內(nèi)存中的各種數(shù)據(jù)類型的數(shù)據(jù)通過網(wǎng)絡(luò)傳送給其它機(jī)器或客戶端;
  2. 把內(nèi)存中的各種數(shù)據(jù)類型的數(shù)據(jù)保存到本地磁盤持久化;

2.數(shù)據(jù)格式

如果要將一個系統(tǒng)內(nèi)的數(shù)據(jù)通過網(wǎng)絡(luò)傳輸給其它系統(tǒng)或客戶端,我們通常都需要先把這些數(shù)據(jù)轉(zhuǎn)化為字符串或字節(jié)串,而且需要規(guī)定一種統(tǒng)一的數(shù)據(jù)格式才能讓數(shù)據(jù)接收端正確解析并理解這些數(shù)據(jù)的含義。XML 是早期被廣泛使用的數(shù)據(jù)交換格式,在早期的系統(tǒng)集成論文中經(jīng)常可以看到它的身影;如今大家使用更多的數(shù)據(jù)交換格式是JSON(JavaScript Object Notation),它是一種輕量級的數(shù)據(jù)交換格式。JSON相對于XML而言,更加加單、易于閱讀和編寫,同時也易于機(jī)器解析和生成。除此之外,我們也可以自定義內(nèi)部使用的數(shù)據(jù)交換格式。

如果是想把數(shù)據(jù)持久化到本地磁盤,這部分?jǐn)?shù)據(jù)通常只是供系統(tǒng)內(nèi)部使用,因此數(shù)據(jù)轉(zhuǎn)換協(xié)議以及轉(zhuǎn)換后的數(shù)據(jù)格式也就不要求是標(biāo)準(zhǔn)、統(tǒng)一的,只要本系統(tǒng)內(nèi)部能夠正確識別即可。但是,系統(tǒng)內(nèi)部的轉(zhuǎn)換協(xié)議通常會隨著編程語言版本的升級而發(fā)生變化(改進(jìn)算法、提高效率),因此通常會涉及轉(zhuǎn)換協(xié)議與編程語言的版本兼容問題,下面要時候的pickle協(xié)議就是這樣一個例子。

3. 序列化/反序列化

將對象轉(zhuǎn)換為可通過網(wǎng)絡(luò)傳輸或可以存儲到本地磁盤的數(shù)據(jù)格式(如:XML、JSON或特定格式的字節(jié)串)的過程稱為序列化;反之,則稱為反序列化。

4.相關(guān)模塊

本節(jié)要介紹的就是Python內(nèi)置的幾個用于進(jìn)行數(shù)據(jù)序列化的模塊:

 

模塊名稱 描述 提供的api
json 用于實現(xiàn)Python數(shù)據(jù)類型與通用(json)字符串之間的轉(zhuǎn)換 dumps()、dump()、loads()、load()
pickle 用于實現(xiàn)Python數(shù)據(jù)類型與Python特定二進(jìn)制格式之間的轉(zhuǎn)換 dumps()、dump()、loads()、load()
shelve 專門用于將Python數(shù)據(jù)類型的持久化到磁盤,shelf是一個類似dict的對象,操作十分便捷 open()

 

二、json模塊

大部分編程語言都會提供處理json數(shù)據(jù)的接口,Python 2.6開始加入了json模塊,且把它作為一個內(nèi)置模塊提供,無需下載即可使用。

1. 序列化與反序列化

Python的JSON模塊 序列化與反序列化的過程分別叫做:encoding 和 decoding。

  1. encoding: 把Python對象轉(zhuǎn)換成JSON字符串
  2. decoding: 把JSON字符串轉(zhuǎn)換成python對象

json模塊提供了以下兩個方法來進(jìn)行序列化和反序列化操作:

?
1
2
3
4
5
# 序列化:將Python對象轉(zhuǎn)換成json字符串
dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
 
# 反序列化:將json字符串轉(zhuǎn)換成Python對象
loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)

除此之外,json模塊還提供了兩個額外的方法允許我們直接將序列化后得到的json數(shù)據(jù)保存到文件中,以及直接讀取文件中的json數(shù)據(jù)進(jìn)行反序列化操作:

?
1
2
3
4
5
# 序列化:將Python對象轉(zhuǎn)換成json字符串并存儲到文件中
dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
 
# 反序列化:讀取指定文件中的json字符串并轉(zhuǎn)換成Python對象
load(fp, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)

2. JSON與Python之間數(shù)據(jù)類型對應(yīng)關(guān)系

Python轉(zhuǎn)JSON

 

Python JSON
dict Object
list, tuple array
str string
int, float, int- & float-derived Enums numbers
True true
False false
None null

 

JSON轉(zhuǎn)Python

 

JSON Python
object dict
array list
string str
number(int) int
number(real) float
true True
false False
null None

 

說明:

  1. Python dict中的非字符串key被轉(zhuǎn)換成JSON字符串時都會被轉(zhuǎn)換為小寫字符串;
  2. Python中的tuple,在序列化時會被轉(zhuǎn)換為array,但是反序列化時,array會被轉(zhuǎn)化為list;
  3. 由以上兩點可知,當(dāng)Python對象中包含tuple數(shù)據(jù)或者包含dict,且dict中存在非字符串的key時,反序列化后得到的結(jié)果與原來的Python對象是不一致的;
  4. 對于Python內(nèi)置的數(shù)據(jù)類型(如:str, unicode, int, float, bool, None, list, tuple, dict)json模塊可以直接進(jìn)行序列化/反序列化處理;對于自定義類的對象進(jìn)行序列化和反序列化時,需要我們自己定義一個方法來完成定義object和dict之間進(jìn)行轉(zhuǎn)化。

3. 實例:內(nèi)置數(shù)據(jù)類型序列化/反序列化

序列化

?
1
2
3
# 序列化
>>> json.dumps({'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)})
'{"a": "str", "c": true, "b": 11.1, "e": 10, "d": null, "g": [4, 5, 6], "f": [1, 2, 3]}'

sort_keys參數(shù): 表示序列化時是否對dict的key進(jìn)行排序(dict默認(rèn)是無序的)

?
1
2
3
# 序列化并對key進(jìn)行排序
>>> json.dumps({'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}, sort_keys=True)
'{"a": "str", "b": 11.1, "c": true, "d": null, "e": 10, "f": [1, 2, 3], "g": [4, 5, 6]}'

indent參數(shù): 表示縮進(jìn)的意思,它可以使得數(shù)據(jù)存儲的格式變得更加優(yōu)雅、可讀性更強(qiáng);如果indent是一個非負(fù)整數(shù)或字符串,則JSON array元素和object成員將會被以相應(yīng)的縮進(jìn)級別進(jìn)行打印輸出;如果indent是0或負(fù)數(shù)或空字符串,則將只會插入換行,不會有縮進(jìn)。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 序列化并對key進(jìn)行排序及格式化輸出
>>> print(json.dumps({'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}, sort_keys=True, indent=4))
{
 "a": "str",
 "b": 11.1,
 "c": true,
 "d": null,
 "e": 10,
 "f": [
  1,
  2,
  3
 ],
 "g": [
  4,
  5,
  6
 ]
}

separators參數(shù): 盡管indent參數(shù)可以使得數(shù)據(jù)存儲的格式變得更加優(yōu)雅、可讀性更強(qiáng),但是那是通過添加一些冗余的空白字符進(jìn)行填充的。當(dāng)json被用于網(wǎng)絡(luò)數(shù)據(jù)通信時,應(yīng)該盡可能的減少無用的數(shù)據(jù)傳輸,這樣可以節(jié)省貸款并加快數(shù)據(jù)傳輸速度。json模塊序列化Python對象后得到的json字符串中的','號和':'號分隔符后默認(rèn)都會附加一個空白字符,我們可以通過separators參數(shù)重新指定分隔符,從而去除無用的空白字符;

  1. 該參數(shù)的值應(yīng)該是一個tuple(item_separator, key_separator)
  2. 如果indent是None,其默認(rèn)值為(', ', ': ')
  3. 如果indent不為None,則默認(rèn)值為(',', ': ')
  4. 我們可以通過為separator賦值為(',', ':')來消除空白字符
?
1
2
3
4
5
>>> json.dumps({'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)})
'{"a": "str", "c": true, "b": 11.1, "e": 10, "d": null, "g": [4, 5, 6], "f": [1, 2, 3]}'
 
>>> json.dumps({'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}, separators=(',',':'))
'{"a":"str","c":true,"b":11.1,"e":10,"d":null,"g":[4,5,6],"f":[1,2,3]}'

反序列化

?
1
2
3
4
5
6
# 反序列化
>>> json.loads('{"a": "str", "c": true, "b": 11.1, "e": 10, "d": null, "g": [4, 5, 6], "f": [1, 2, 3]}')
{'c': True, 'e': 10, 'a': 'str', 'g': [4, 5, 6], 'd': None, 'f': [1, 2, 3], 'b': 11.1}
 
>>> json.loads('{"a":"str","c":true,"b":11.1,"e":10,"d":null,"g":[4,5,6],"f":[1,2,3]}')
{'c': True, 'e': 10, 'a': 'str', 'g': [4, 5, 6], 'd': None, 'f': [1, 2, 3], 'b': 11.1}

dump()與load()函數(shù)示例

?
1
2
3
4
5
6
7
8
# 序列化到文件中
>>> with open('test.json', 'w') as fp:
...  json.dump({'a':'str中國', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}, fp, indent=4)
 
# 反序列化文件中的內(nèi)容
>>> with open('test.json', 'r') as fp:
...  json.load(fp)
{'e': 10, 'g': [4, 5, 6], 'b': 11.1, 'c': True, 'd': None, 'a': 'str中國', 'f': [1, 2, 3]}

需要說明的是: 如果試圖使用相同的fp重復(fù)調(diào)用dump()函數(shù)去序列化多個對象(或序列化同一個對象多次),將會產(chǎn)生一個無效的JSON文件,也就是說對于一個fp只能調(diào)用一次dump()。

4. 實例:自定義數(shù)據(jù)類型的序列化/反序列化

Python是面向?qū)ο蟮木幊陶Z言,我們可以自定義需要的數(shù)據(jù)類型;實際工作中,我們常常會用到自定義數(shù)據(jù)類型的序列化與反序列化操作。要實現(xiàn)自定義數(shù)據(jù)類型的序列化與反序列化有兩種方式:

  1. 通過轉(zhuǎn)換函數(shù)實現(xiàn)
  2. 通過繼承JSONEncoder和JSONDecoder類實現(xiàn)

首先來自定義一個數(shù)據(jù)類型

?
1
2
3
4
5
6
7
8
class Student(object):
 def __init__(self, name, age, sno):
  self.name = name
  self.age = age
  self.sno = sno
 
 def __repr__(self):
  return 'Student [name: %s, age: %d, sno: %d]' % (self.name, self.age, self.sno)

直接調(diào)用dumps()方法會引發(fā)TypeError錯誤:

?
1
2
3
4
5
6
7
>>> stu = Student('Tom', 19, 1)
>>> print(stu)
Student [name: Tom, age: 19, sno: 1]
>>>
>>> json.dumps(stu)
...
TypeError: Student [name: Tom, age: 19, sno: 1] is not JSON serializable

上面的異常信息中指出:stu對象不可以被序列化為JSON個數(shù)的數(shù)據(jù)。那么我們分別通過“編寫轉(zhuǎn)換函數(shù)” 和 “繼承JSONEncoder和JSONDecoder類” 來實現(xiàn)對這個自定義數(shù)據(jù)類型的JSON序列化和反序列化。

方法1:編寫轉(zhuǎn)換函數(shù)

那么這個轉(zhuǎn)換函數(shù)要完成哪兩個數(shù)據(jù)類型之間的轉(zhuǎn)換呢? 從上面列出的JSON與Python數(shù)據(jù)類型的對應(yīng)表中可知,JSON中的object對應(yīng)的是Python中的dict,因此要對Python中的自定義數(shù)據(jù)類型的對象進(jìn)行序列化,就需要先把這個對象轉(zhuǎn)換成json模塊可以直接進(jìn)行序列化dict類型。由此可知,這個轉(zhuǎn)換函數(shù)是要完成的是Python對象(不是JSON對象)與dict之間的相互轉(zhuǎn)換,且序列化時轉(zhuǎn)換過程是“Python對象 --> dict --> JSON object”,反序列化的過程是“JSON object -> dict --> Python對象”。所以,我們需要編寫兩個轉(zhuǎn)換函數(shù)來分別實現(xiàn)序列化和反序列化時的轉(zhuǎn)換過程。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def obj2dict(obj):
 d = {}
 d['__class__'] = obj.__class__.__name__
 d['__module__'] = obj.__module__
 d.update(obj.__dict__)
 return d
 
def dict2obj(d):
 if '__class__' in d:
  class_name = d.pop('__class__')
  module_name = d.pop('__module__')
  module = __import__(module_name)
  class_ = getattr(module, class_name)
  args = dict((key.encode('ascii'), value) for key, value in d.items())
  instance = class_(**args)
 else:
  instance = d
 return instance

繼承JSONEncoder實現(xiàn)反序列化時還有一個額外的作用,就是可以通過iterencode()方法把一個很大的數(shù)據(jù)對象分多次進(jìn)行序列化,這對于網(wǎng)絡(luò)傳輸、磁盤持久化等情景非常有用。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> for chunk in MyJSONEncoder().iterencode(stu):
...  print(chunk)
...
{
"__class__"
:
"Student"
,
"name"
:
"Tom"
,
"__module__"
:
"__main__"
,
"sno"
:
1
,
"age"
:
19
}

大數(shù)據(jù)對象序列化網(wǎng)絡(luò)傳輸偽代碼:

?
1
2
for chunk in JSONEncoder().iterencode(bigobject):
 mysocket.write(chunk)

序列化測試:

?
1
2
3
4
5
6
7
8
9
10
>>> import json
 
>>> obj2dict(stu)
{'sno': 1, '__module__': '__main__', 'age': 19, '__class__': 'Student', 'name': 'Tom'}
 
>>> json.dumps(obj2dict(stu))
'{"sno": 1, "__module__": "__main__", "age": 19, "__class__": "Student", "name": "Tom"}'
 
>>> json.dumps(stu, default=obj2dict)
'{"sno": 1, "__module__": "__main__", "age": 19, "__class__": "Student", "name": "Tom"}'

json.dumps(stu, default=obj2dict) 等價于 json.dumps(obj2dict(stu))

反序列化測試:

?
1
2
3
4
5
6
7
8
>>> json.loads('{"sno": 1, "__module__": "__main__", "age": 19, "__class__": "Student", "name": "Tom"}')
{u'sno': 1, u'__module__': u'__main__', u'age': 19, u'name': u'Tom', u'__class__': u'Student'}
 
>>> dict2obj(json.loads('{"sno": 1, "__module__": "__main__", "age": 19, "__class__": "Student", "name": "Tom"}'))
Student [name: Tom, age: 19, sno: 1]
 
>>> json.loads('{"sno": 1, "__module__": "__main__", "age": 19, "__class__": "Student", "name": "Tom"}', object_hook=dict2obj)
Student [name: Tom, age: 19, sno: 1]

json.loads(JSON_STR, object_hook=dict2obj) 等價于 dict2obj(json.loads(JSON_STR))

方法2:繼承JSONEncoder和JSONDecoder實現(xiàn)子類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import json
 
class MyJSONEncoder(json.JSONEncoder):
 def default(self, obj):
  d = {}
  d['__class__'] = obj.__class__.__name__
  d['__module__'] = obj.__module__
  d.update(obj.__dict__)
  return d
 
class MyJSONDecoder(json.JSONDecoder):
 def __init__(self):
  json.JSONDecoder.__init__(self, object_hook=self.dict2obj)
 
 def dict2obj(self, d):
  if '__class__' in d:
   class_name = d.pop('__class__')
   module_name = d.pop('__module__')
   module = __import__(module_name)
   class_ = getattr(module, class_name)
   args = dict((key.encode('ascii'), value) for key, value in d.items())
   instance = class_(**args)
  else:
   instance = d
  return instance

序列化測試:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
>>> stu = Student('Tom', 19, 1)
 
# 方式一:直接調(diào)用子類MyJSONEncoder的encode()方法進(jìn)行序列化
>>> MyJSONEncoder().encode(stu)
'{"__class__": "Student", "__module__": "__main__", "name": "Tom", "age": 19, "sno": 1}'
>>> MyJSONEncoder(separators=(',', ':')).encode(stu)
'{"__class__":"Student","__module__":"__main__","name":"Tom","age":19,"sno":1}'
 
# 方式二:將子類MyJSONEncoder作為cls參數(shù)的值傳遞給json.dumps()函數(shù)
>>> json.dumps(stu, cls=MyJSONEncoder)
'{"__class__": "Student", "__module__": "__main__", "name": "Tom", "age": 19, "sno": 1}'
>>> json.dumps(stu, cls=MyJSONEncoder, separators=(',', ':'))
'{"__class__":"Student","__module__":"__main__","name":"Tom","age":19,"sno":1}'

反序列化測試:

?
1
2
>>> MyJSONDecoder().decode('{"sno": 1, "__module__": "__main__", "age": 19, "__class__": "Student", "name": "Tom"}')
Student [name: Tom, age: 19, sno: 1]

說明: 經(jīng)過測試發(fā)現(xiàn)MyJSONDecoder().decode(JSON_STR) json.loads(JSON_STR, object_hook=dict2obj) 只能在Python 2.7上正確執(zhí)行,在Python 3.5上無法正確執(zhí)行;而 json.loads(JSON_STR, cls=MyJSONDecoder) 無論在Python 2.7還是在Python 3.5上都無法正確執(zhí)行。這說明json模塊對于自定義數(shù)據(jù)類型的反序列化支持還是比較有限的,但是我們也可以通過json.loads(JSON_STR)函數(shù),不指定cls參數(shù)來得到一個dict對象,然后自己完成dict到object的轉(zhuǎn)換。

三、pickle模塊

pickle模塊實現(xiàn)了用于對Python對象結(jié)構(gòu)進(jìn)行 序列化 和 反序列化 的二進(jìn)制協(xié)議,與json模塊不同的是pickle模塊序列化和反序列化的過程分別叫做 pickling 和 unpickling:

  1. pickling: 是將Python對象轉(zhuǎn)換為字節(jié)流的過程;
  2. unpickling: 是將字節(jié)流二進(jìn)制文件或字節(jié)對象轉(zhuǎn)換回Python對象的過程;

1. pickle模塊與json模塊對比

  1. JSON是一種文本序列化格式(它輸出的是unicode文件,大多數(shù)時候會被編碼為utf-8),而pickle是一個二進(jìn)制序列化格式;
  2. JOSN是我們可以讀懂的數(shù)據(jù)格式,而pickle是二進(jìn)制格式,我們無法讀懂;
  3. JSON是與特定的編程語言或系統(tǒng)無關(guān)的,且它在Python生態(tài)系統(tǒng)之外被廣泛使用,而pickle使用的數(shù)據(jù)格式是特定于Python的;
  4. 默認(rèn)情況下,JSON只能表示Python內(nèi)建數(shù)據(jù)類型,對于自定義數(shù)據(jù)類型需要一些額外的工作來完成;pickle可以直接表示大量的Python數(shù)據(jù)類型,包括自定數(shù)據(jù)類型(其中,許多是通過巧妙地使用Python內(nèi)省功能自動實現(xiàn)的;復(fù)雜的情況可以通過實現(xiàn)specific object API來解決)

2. pickle模塊使用的數(shù)據(jù)流格式

上面提到,pickle使用的數(shù)據(jù)格式是特定于Python的。這使得它不受諸如JSON或XDR的外部標(biāo)準(zhǔn)限值,但是這也意味著非Python程序可能無法重建pickled Python對象。默認(rèn)情況下,pickle數(shù)據(jù)格式使用相對緊湊的二進(jìn)制表示。如果需要最佳大小特征,可以有效的壓縮pickled數(shù)據(jù)。pickletools模塊包含可以用于對pickle生成的數(shù)據(jù)流進(jìn)行分析的工具。目前有5種不同的協(xié)議可以用于pickle。使用的協(xié)議越高,就需要更新的Python版本去讀取pickle產(chǎn)生的數(shù)據(jù):

  1. 協(xié)議v0是原始的“人類可讀”協(xié)議,并且向后倩蓉早期的Python版本;
  2. 協(xié)議v1是一個舊的二進(jìn)制格式,也與早期版本的Python兼容;
  3. 協(xié)議v2在Python 2.3中引入,它提供更高效的pickling;
  4. 協(xié)議v3是在Python 3.0添加的協(xié)議,它明確支持bytes對象,且不能被Python 2.x 進(jìn)行unpickle操作;這是默認(rèn)協(xié)議,也是當(dāng)需要兼容其他Python 3版本時被推薦使用的協(xié)議;
  5. 協(xié)議4是在Python 3.4添加的協(xié)議,它添加了對極大對象的支持,pickling更多種類的對象,以及一些數(shù)據(jù)格式的優(yōu)化。

說明: Python 2.x中默認(rèn)使用的是協(xié)議v0,如果協(xié)議指定為賦值或HIGHEST_PROTOCOL,將使用當(dāng)前可用的最高協(xié)議版本;Python 3.x中默認(rèn)使用的是協(xié)議v3,它兼容其他Python 3版本,但是不兼容Python 2。

注意: 序列化(Serialization)是一個比持久化(Persistence)更加原始的概念;雖然pickle可以讀寫文件對象,但是它不處理持久化對象的命名問題,也不處理對持久化對象的并發(fā)訪問問題(甚至更復(fù)雜的問題)。pickle模塊可以將復(fù)雜對象轉(zhuǎn)換為字節(jié)流,并且可以將字節(jié)流轉(zhuǎn)換為具有相同內(nèi)部結(jié)構(gòu)的對象。或許最可能對這些字節(jié)流做的事情是將它們寫入文件,但是也可以對它們進(jìn)行網(wǎng)絡(luò)傳輸或?qū)⑺鼈兇鎯υ跀?shù)據(jù)庫中。shelve模塊提供了一個簡單的接口用于在DBM風(fēng)格的數(shù)據(jù)庫文件上對對象進(jìn)行pickle和unpickle操作。

3. pickle模塊提供的相關(guān)函數(shù)

pickle模塊提供的幾個序列化/反序列化的函數(shù)與json模塊基本一致:

?
1
2
3
4
5
6
7
8
9
10
11
# 將指定的Python對象通過pickle序列化作為bytes對象返回,而不是將其寫入文件
dumps(obj, protocol=None, *, fix_imports=True)
 
# 將通過pickle序列化后得到的字節(jié)對象進(jìn)行反序列化,轉(zhuǎn)換為Python對象并返回
loads(bytes_object, *, fix_imports=True, encoding="ASCII", errors="strict")
 
# 將指定的Python對象通過pickle序列化后寫入打開的文件對象中,等價于`Pickler(file, protocol).dump(obj)`
dump(obj, file, protocol=None, *, fix_imports=True)
 
# 從打開的文件對象中讀取pickled對象表現(xiàn)形式并返回通過pickle反序列化后得到的Python對象
load(file, *, fix_imports=True, encoding="ASCII", errors="strict")

說明: 上面這幾個方法參數(shù)中,*號后面的參數(shù)都是Python 3.x新增的,目的是為了兼容Python 2.x,具體用法請參看官方文檔。

4. 實例:內(nèi)置數(shù)據(jù)類型的序列化/反序列化

Python 2.x

?
1
2
3
4
5
6
7
8
9
10
11
12
13
>>> import pickle
>>>
>>> var_a = {'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}
 
# 序列化
>>> var_b = pickle.dumps(var_a)
>>> var_b
"(dp0\nS'a'\np1\nS'str'\np2\nsS'c'\np3\nI01\nsS'b'\np4\nF11.1\nsS'e'\np5\nI10\nsS'd'\np6\nNsS'g'\np7\n(I4\nI5\nI6\ntp8\nsS'f'\np9\n(lp10\nI1\naI2\naI3\nas."
 
# 反序列化
>>> var_c = pickle.loads(var_b)
>>> var_c
{'a': 'str', 'c': True, 'b': 11.1, 'e': 10, 'd': None, 'g': (4, 5, 6), 'f': [1, 2, 3]}

Python 3.x

?
1
2
3
4
5
6
7
8
9
10
11
12
13
>>> import pickle
>>>
>>> var_a = {'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}
 
# 序列化
>>> var_b = pickle.dumps(var_a)
>>> var_b
b'\x80\x03}q\x00(X\x01\x00\x00\x00eq\x01K\nX\x01\x00\x00\x00aq\x02X\x03\x00\x00\x00strq\x03X\x01\x00\x00\x00fq\x04]q\x05(K\x01K\x02K\x03eX\x01\x00\x00\x00gq\x06K\x04K\x05K\x06\x87q\x07X\x01\x00\x00\x00bq\x08G@&333333X\x01\x00\x00\x00cq\t\x88X\x01\x00\x00\x00dq\nNu.'
 
# 反序列化
>>> var_c = pickle.loads(var_b)
>>> var_c
{'e': 10, 'a': 'str', 'f': [1, 2, 3], 'g': (4, 5, 6), 'b': 11.1, 'c': True, 'd': None}

dump()與load()

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> import pickle
>>>
>>> var_a = {'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}
 
# 持久化到文件
>>> with open('pickle.txt', 'wb') as f:
...  pickle.dump(var_a, f)
...
 
# 從文件中讀取數(shù)據(jù)
>>> with open('pickle.txt', 'rb') as f:
...  var_b = pickle.load(f)
...
>>> var_b
{'e': 10, 'a': 'str', 'f': [1, 2, 3], 'g': (4, 5, 6), 'b': 11.1, 'c': True, 'd': None}
>>>

 

說明:

  1. 默認(rèn)情況下Python 2.x中pickled后的數(shù)據(jù)是字符串形式,需要將它轉(zhuǎn)換為字節(jié)對象才能被Python 3.x中的pickle.loads()反序列化;Python 3.x中pickling所使用的協(xié)議是v3,因此需要在調(diào)用pickle.dumps()時指定可選參數(shù)protocol為Python 2.x所支持的協(xié)議版本(0,1,2),否則pickled后的數(shù)據(jù)不能被被Python 2.x中的pickle.loads()反序列化;
  2. Python 3.x中pickle.dump()和pickle.load()方法中指定的文件對象,必須以二進(jìn)制模式打開,而Python 2.x中可以以二進(jìn)制模式打開,也可以以文本模式打開。

5. 實例:自定義數(shù)據(jù)類型的序列化/反序列化

首先來自定義一個數(shù)據(jù)類型:

?
1
2
3
4
5
6
7
8
class Student(object):
 def __init__(self, name, age, sno):
  self.name = name
  self.age = age
  self.sno = sno
 
 def __repr__(self):
  return 'Student [name: %s, age: %d, sno: %d]' % (self.name, self.age, self.sno)

pickle模塊可以直接對自定數(shù)據(jù)類型進(jìn)行序列化/反序列化操作,無需編寫額外的處理函數(shù)或類。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> stu = Student('Tom', 19, 1)
>>> print(stu)
Student [name: Tom, age: 19, sno: 1]
 
# 序列化
>>> var_b = pickle.dumps(stu)
>>> var_b
b'\x80\x03c__main__\nStudent\nq\x00)\x81q\x01}q\x02(X\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00Tomq\x04X\x03\x00\x00\x00ageq\x05K\x13X\x03\x00\x00\x00snoq\x06K\x01ub.'
 
# 反序列化
>>> var_c = pickle.loads(var_b)
>>> var_c
Student [name: Tom, age: 19, sno: 1]
 
# 持久化到文件
>>> with open('pickle.txt', 'wb') as f:
...  pickle.dump(stu, f)
...
 
# 從文件總讀取數(shù)據(jù)
>>> with open('pickle.txt', 'rb') as f:
...  pickle.load(f)
...
Student [name: Tom, age: 19, sno: 1]

四、shelve模塊

shelve是一個簡單的數(shù)據(jù)存儲方案,類似key-value數(shù)據(jù)庫,可以很方便的保存python對象,其內(nèi)部是通過pickle協(xié)議來實現(xiàn)數(shù)據(jù)序列化。shelve只有一個open()函數(shù),這個函數(shù)用于打開指定的文件(一個持久的字典),然后返回一個shelf對象。shelf是一種持久的、類似字典的對象。它與“dbm”的不同之處在于,其values值可以是任意基本Python對象--pickle模塊可以處理的任何數(shù)據(jù)。這包括大多數(shù)類實例、遞歸數(shù)據(jù)類型和包含很多共享子對象的對象。keys還是普通的字符串。

?
1
open(filename, flag='c', protocol=None, writeback=False)

flag 參數(shù)表示打開數(shù)據(jù)存儲文件的格式,可取值與dbm.open()函數(shù)一致:

 

描述
'r' 以只讀模式打開一個已經(jīng)存在的數(shù)據(jù)存儲文件
'w' 以讀寫模式打開一個已經(jīng)存在的數(shù)據(jù)存儲文件
'c' 以讀寫模式打開一個數(shù)據(jù)存儲文件,如果不存在則創(chuàng)建
'n' 總是創(chuàng)建一個新的、空數(shù)據(jù)存儲文件,并以讀寫模式打開
 

 

protocol 參數(shù)表示序列化數(shù)據(jù)所使用的協(xié)議版本,默認(rèn)是pickle v3;

writeback 參數(shù)表示是否開啟回寫功能。

我們可以把shelf對象當(dāng)dict來使用--存儲、更改、查詢某個key對應(yīng)的數(shù)據(jù),當(dāng)操作完成之后,調(diào)用shelf對象的close()函數(shù)即可。當(dāng)然,也可以使用上下文管理器(with語句),避免每次都要手動調(diào)用close()方法。

實例:內(nèi)置數(shù)據(jù)類型操作

?
1
2
3
4
5
6
7
8
9
10
11
# 保存數(shù)據(jù)
with shelve.open('student') as db:
 db['name'] = 'Tom'
 db['age'] = 19
 db['hobby'] = ['籃球', '看電影', '彈吉他']
 db['other_info'] = {'sno': 1, 'addr': 'xxxx'}
 
# 讀取數(shù)據(jù)
with shelve.open('student') as db:
 for key,value in db.items():
  print(key, ': ', value)

輸出結(jié)果:

name :  Tom
age :  19
hobby :  ['籃球', '看電影', '彈吉他']
other_info :  {'sno': 1, 'addr': 'xxxx'}

實例:自定義數(shù)據(jù)類型操作

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 自定義class
class Student(object):
 def __init__(self, name, age, sno):
  self.name = name
  self.age = age
  self.sno = sno
 
 def __repr__(self):
  return 'Student [name: %s, age: %d, sno: %d]' % (self.name, self.age, self.sno)
 
# 保存數(shù)據(jù)
tom = Student('Tom', 19, 1)
jerry = Student('Jerry', 17, 2)
 
with shelve.open("stu.db") as db:
 db['Tom'] = tom
 db['Jerry'] = jerry
 
# 讀取數(shù)據(jù)
with shelve.open("stu.db") as db:
 print(db['Tom'])
 print(db['Jerry'])

輸出結(jié)果:
Student [name: Tom, age: 19, sno: 1]
Student [name: Jerry, age: 17, sno: 2]

五、總結(jié)

1. 對比

json模塊常用于編寫web接口,將Python數(shù)據(jù)轉(zhuǎn)換為通用的json格式傳遞給其它系統(tǒng)或客戶端;也可以用于將Python數(shù)據(jù)保存到本地文件中,缺點是明文保存,保密性差。另外,如果需要保存費(fèi)內(nèi)置數(shù)據(jù)類型需要編寫額外的轉(zhuǎn)換函數(shù)或自定義類。

pickle模塊和shelve模塊由于使用其特有的序列化協(xié)議,其序列化之后的數(shù)據(jù)只能被Python識別,因此只能用于Python系統(tǒng)內(nèi)部。另外,Python 2.x 和 Python
 3.x 默認(rèn)使用的序列化協(xié)議也不同,如果需要互相兼容需要在序列化時通過protocol參數(shù)指定協(xié)議版本。除了上面這些缺點外,pickle模塊和shelve模塊相對于json模塊的優(yōu)點在于對于自定義數(shù)據(jù)類型可以直接序列化和反序列化,不需要編寫額外的轉(zhuǎn)換函數(shù)或類。

shelve模塊可以看做是pickle模塊的升級版,因為shelve使用的就是pickle的序列化協(xié)議,但是shelve比pickle提供的操作方式更加簡單、方便。shelve模塊相對于其它兩個模塊在將Python數(shù)據(jù)持久化到本地磁盤時有一個很明顯的優(yōu)點就是,它允許我們可以像操作dict一樣操作被序列化的數(shù)據(jù),而不必一次性的保存或讀取所有數(shù)據(jù)。

2. 建議

  1. 需要與外部系統(tǒng)交互時用json模塊;
  2. 需要將少量、簡單Python數(shù)據(jù)持久化到本地磁盤文件時可以考慮用pickle模塊;
  3. 需要將大量Python數(shù)據(jù)持久化到本地磁盤文件或需要一些簡單的類似數(shù)據(jù)庫的增刪改查功能時,可以考慮用shelve模塊。

3. 附錄

 

要實現(xiàn)的功能 可以使用的api
將Python數(shù)據(jù)類型轉(zhuǎn)換為(json)字符串 json.dumps()
將json字符串轉(zhuǎn)換為Python數(shù)據(jù)類型 json.loads()
將Python數(shù)據(jù)類型以json形式保存到本地磁盤 json.dump()
將本地磁盤文件中的json數(shù)據(jù)轉(zhuǎn)換為Python數(shù)據(jù)類型 json.load()
將Python數(shù)據(jù)類型轉(zhuǎn)換為Python特定的二進(jìn)制格式 pickle.dumps()
將Python特定的的二進(jìn)制格式數(shù)據(jù)轉(zhuǎn)換為Python數(shù)據(jù)類型 pickle.loads()
將Python數(shù)據(jù)類型以Python特定的二進(jìn)制格式保存到本地磁盤 pickle.dump()
將本地磁盤文件中的Python特定的二進(jìn)制格式數(shù)據(jù)轉(zhuǎn)換為Python數(shù)據(jù)類型 pickle.load()
以類型dict的形式將Python數(shù)據(jù)類型保存到本地磁盤或讀取本地磁盤數(shù)據(jù)并轉(zhuǎn)換為數(shù)據(jù)類型 shelve.open()

 

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。

原文鏈接:http://www.cnblogs.com/yyds/p/6563608.html

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 国产xxxx成人精品免费视频频 | 玖玖精品视频 | 中文字幕成人 | 成年免费视频 | 这里只有精品久久 | 一级片黄色免费 | 特黄特黄aaaa级毛片免费看 | 亚洲高清电影 | 中文字幕1区2区3区 亚洲欧美日韩精品久久亚洲区 | 欧美成人激情 | 91在线免费播放 | 麻豆av电影在线观看 | 精品日韩一区 | 久久综合亚洲精品 | 免费网站在线观看黄 | 日韩在线字幕 | 亚洲成人免费 | 久久91久久久久麻豆精品 | 亚洲永久免费 | 欧美日韩精品一区二区三区蜜桃 | 成人欧美一区二区三区在线播放 | 最新黄色网址在线播放 | 高清av在线 | 激情小视频 | 夜夜摸夜夜操 | 精品一区二区三区免费视频 | 一区在线视频 | 日韩精品在线视频 | 免费激情 | 欧美成人高清视频 | 国产一极片 | 精品综合久久 | 伊人精品成人久久综合软件 | 国产一区二区三区免费在线观看 | 久久久无码精品亚洲日韩按摩 | 视频一区二区三区在线播放 | 午夜草逼| 国产精品香蕉 | 亚洲一区二区在线免费观看 | av电影一区二区 | 国产成人精品免高潮在线观看 |