Skip to content

Commit 194d8e3

Browse files
committed
pickle,shelve
1 parent 52aa914 commit 194d8e3

File tree

6 files changed

+10219
-0
lines changed

6 files changed

+10219
-0
lines changed

229.md

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
>要常常喜乐,不住地祷告,凡事谢恩,因为这是神在基督耶稣里向你们所定的旨意。不要消灭圣灵的感动,不要藐视先知的讲论。但要凡事察验,善美的要持守,各样的恶事要禁戒不作。(1 THESSALONIANS 5:16-22)
2+
3+
#将数据存入文件
4+
5+
[《文件(1)》](./126.md)[《文件(2)》](./127.md)中,已经学习了如何读写文件。
6+
7+
如果在程序中,有数据要保存到磁盘中,放到某个文件中是一种不错的方法。但是,如果像以前那样存,未免有点凌乱,并且没有什么良好的存储格式,导致数据以后被读出来的时候遇到麻烦,特别是不能让另外的使用者很好地理解。不要忘记了,编程是一个合作的活。还有,存储的数据不一定都是类似字符串、整数那种基础类型的。
8+
9+
总而言之,需要将要存储的对象格式化(或者叫做序列化),才好存好取。这就有点类似集装箱的作用。
10+
11+
所以,要用到本讲中提供的方式。
12+
13+
##pickle
14+
15+
pickle是标准库中的一个模块,还有跟它完全一样的叫做cpickle,两者的区别就是后者更快。所以,下面操作中,不管是用`import pickle`,还是用`import cpickle as pickle`,在功能上都是一样的。
16+
17+
>>> import pickle
18+
>>> integers = [1, 2, 3, 4, 5]
19+
>>> f = open("22901.dat", "wb")
20+
>>> pickle.dump(integers, f)
21+
>>> f.close()
22+
23+
`pickle.dump(integers, f)`将数据integers保存到了文件22901.dat中。如果你要打开这个文件,看里面的内容,可能有点失望,但是,它对计算机是友好的。这个步骤,可以称之为将对象序列化。用到的方法是:
24+
25+
`pickle.dump(obj,file[,protocol])`
26+
27+
- obj:序列化对象,上面的例子中是一个列表,它是基本类型,也可以序列化自己定义的类型。
28+
- file:一般情况下是要写入的文件。更广泛地可以理解为为拥有write()方法的对象,并且能接受字符串为为参数,所以,它还可以是一个StringIO对象,或者其它自定义满足条件的对象。
29+
- protocol:可选项。默认为False(或者说0),是以ASCII格式保存对象;如果设置为1或者True,则以压缩的二进制格式保存对象。
30+
31+
下面换一种数据格式,并且做对比:
32+
33+
>>> import pickle
34+
>>> d = {}
35+
>>> integers = range(9999)
36+
>>> d["i"] = integers #下面将这个dict格式的对象存入文件
37+
38+
>>> f = open("22902.dat", "wb")
39+
>>> pickle.dump(d, f) #文件中以ascii格式保存数据
40+
>>> f.close()
41+
42+
>>> f = open("22903.dat", "wb")
43+
>>> pickle.dump(d, f, True) #文件中以二进制格式保存数据
44+
>>> f.close()
45+
46+
>>> import os
47+
>>> s1 = os.stat("22902.dat").st_size #得到两个文件的大小
48+
>>> s2 = os.stat("22903.dat").st_size
49+
50+
>>> print "%d, %d, %.2f%%" % (s1, s2, (s2+0.0)/s1*100)
51+
68903, 29774, 43.21%
52+
53+
比较结果发现,以二进制方式保存的文件比以ascii格式保存的文件小很多,前者约是后者的43%。
54+
55+
所以,在序列化的时候,特别是面对较大对象时,建议将dump()的参数True设置上,虽然现在存储设备的价格便宜,但是能省还是省点比较好。
56+
57+
存入文件,仅是一个目标,还有另外一个目标,就是要读出来,也称之为反序列化。
58+
59+
>>> integers = pickle.load(open("22901.dat", "rb"))
60+
>>> print integers
61+
[1, 2, 3, 4, 5]
62+
63+
就是前面存入的那个列表。再看看被以二进制存入的那个文件:
64+
65+
>>> f = open("22903.dat", "rb")
66+
>>> d = pickle.load(f)
67+
>>> print d
68+
{'i': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, .... #省略后面的数字}
69+
>>> f.close()
70+
71+
还是有自己定义数据类型的需要,这种类型是否可以用上述方式存入文件并读出来呢?看下面的例子:
72+
73+
>>> import cPickle as pickle #cPickle更快
74+
>>> import StringIO #标准库中的一个模块,跟file功能类似,只不过是在内存中操作“文件”
75+
76+
>>> class Book(object): #自定义一种类型
77+
... def __init__(self,name):
78+
... self.name = name
79+
... def my_book(self):
80+
... print "my book is: ", self.name
81+
...
82+
83+
>>> pybook = Book("<from beginner to master>")
84+
>>> pybook.my_book()
85+
my book is: <from beginner to master>
86+
87+
>>> file = StringIO.StringIO()
88+
>>> pickle.dump(pybook, file, 1)
89+
>>> print file.getvalue() #查看“文件”内容
90+
ccopy_reg
91+
_reconstructor
92+
q(c__main__
93+
Book
94+
qc__builtin__
95+
object
96+
qNtRq}qUnameqU<from beginner to master>sb.
97+
98+
>>> pickle.dump(pybook, file) #换一种方式,再看内容,可以比较一下
99+
>>> print file.getvalue() #视觉上,两者就有很大差异
100+
ccopy_reg
101+
_reconstructor
102+
q(c__main__
103+
Book
104+
qc__builtin__
105+
object
106+
qNtRq}qUnameqU<from beginner to master>sb.ccopy_reg
107+
_reconstructor
108+
p1
109+
(c__main__
110+
Book
111+
p2
112+
c__builtin__
113+
object
114+
p3
115+
NtRp4
116+
(dp5
117+
S'name'
118+
p6
119+
S'<from beginner to master>'
120+
p7
121+
sb.
122+
123+
如果要从文件中读出来:
124+
125+
>>> file.seek(0) #找到对应类型
126+
>>> pybook2 = pickle.load(file)
127+
>>> pybook2.my_book()
128+
my book is: <from beginner to master>
129+
>>> file.close()
130+
131+
##shelve
132+
133+
pickle模块已经表现出它足够好的一面了。不过,由于数据的复杂性,pickle只能完成一部分工作,在另外更复杂的情况下,它就稍显麻烦了。于是,又有了shelve。
134+
135+
shelve模块也是标准库中的。先看一下基本操作:写入和读取
136+
137+
>>> import shelve
138+
>>> s = shelve.open("22901.db")
139+
>>> s["name"] = "www.itdiffer.com"
140+
>>> s["lang"] = "python"
141+
>>> s["pages"] = 1000
142+
>>> s["contents"] = {"first":"base knowledge","second":"day day up"}
143+
>>> s.close()
144+
145+
以上完成了数据写入的过程。其实,这更接近数据库的样式了。下面是读取。
146+
147+
>>> s = shelve.open("22901.db")
148+
>>> name = s["name"]
149+
>>> print name
150+
www.itdiffer.com
151+
>>> contents = s["contents"]
152+
>>> print contents
153+
{'second': 'day day up', 'first': 'base knowledge'}
154+
155+
当然,也可以用for语句来读:
156+
157+
>>> for k in s:
158+
... print k, s[k]
159+
...
160+
contents {'second': 'day day up', 'first': 'base knowledge'}
161+
lang python
162+
pages 1000
163+
name www.itdiffer.com
164+
165+
不管是写,还是读,都似乎要简化了。所建立的对象s,就如同字典一样,可称之为类字典对象。所以,可以如同操作字典那样来操作它。
166+
167+
但是,要小心坑:
168+
169+
>>> f = shelve.open("22901.db")
170+
>>> f["author"]
171+
['qiwsir']
172+
>>> f["author"].append("Hetz") #试图增加一个
173+
>>> f["author"] #坑就在这里
174+
['qiwsir']
175+
>>> f.close()
176+
177+
当试图修改一个已有键的值时,没有报错,但是并没有修改成功。要填平这个坑,需要这样做:
178+
179+
>>> f = shelve.open("22901.db", writeback=True) #多一个参数True
180+
>>> f["author"].append("Hetz")
181+
>>> f["author"] #没有坑了
182+
['qiwsir', 'Hetz']
183+
>>> f.close()
184+
185+
还用for循环一下:
186+
187+
>>> f = shelve.open("22901.db")
188+
>>> for k,v in f.items():
189+
... print k,": ",v
190+
...
191+
contents : {'second': 'day day up', 'first': 'base knowledge'}
192+
lang : python
193+
pages : 1000
194+
author : ['qiwsir', 'Hetz']
195+
name : www.itdiffer.com
196+
197+
shelve更像数据库了。
198+
199+
不过,它还不是真正的数据库。真正的数据库在后面。
200+
201+
------
202+
203+
[总目录](./index.md)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[上节:第三方库](./228.md)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[下节:mysql数据库](./230.md)
204+
205+
如果你认为有必要打赏我,请通过支付宝:**qiwsir@126.com**,不胜感激。

2code/22901.dat

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
(lp0
2+
I1
3+
aI2
4+
aI3
5+
aI4
6+
aI5
7+
a.

2code/22901.db

12 KB
Binary file not shown.

0 commit comments

Comments
 (0)