|
| 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) | [上节:第三方库](./228.md) | [下节:mysql数据库](./230.md) |
| 204 | + |
| 205 | +如果你认为有必要打赏我,请通过支付宝:**qiwsir@126.com**,不胜感激。 |
0 commit comments