鍍金池/ 教程/ Python/ 將數(shù)據(jù)存入文件
標準庫 (4)
如何成為 Python 高手
標準庫 (6)
標準庫 (3)
類(2)
Pandas 使用 (2)
xml
用 tornado 做網(wǎng)站 (5)
文件(1)
練習
列表(3)
從小工到專家
除法
錯誤和異常 (2)
函數(shù)(1)
用 tornado 做網(wǎng)站 (7)
為做網(wǎng)站而準備
函數(shù)練習
標準庫 (8)
Pandas 使用 (1)
回顧 list 和 str
字典(1)
用 tornado 做網(wǎng)站 (3)
字符串(1)
函數(shù)(2)
寫一個簡單的程序
將數(shù)據(jù)存入文件
語句(5)
SQLite 數(shù)據(jù)庫
集成開發(fā)環(huán)境(IDE)
集合(1)
類(1)
用 tornado 做網(wǎng)站 (6)
用 tornado 做網(wǎng)站 (2)
自省
語句(4)
錯誤和異常 (1)
用 tornado 做網(wǎng)站 (4)
集合(2)
列表(1)
標準庫 (1)
生成器
mysql 數(shù)據(jù)庫 (1)
第三方庫
實戰(zhàn)
運算符
類(3)
字典(2)
語句(1)
數(shù)和四則運算
語句(2)
文件(2)
MySQL 數(shù)據(jù)庫 (2)
電子表格
迭代器
mongodb 數(shù)據(jù)庫 (1)
特殊方法 (2)
特殊方法 (1)
字符編碼
編寫模塊
用 tornado 做網(wǎng)站 (1)
標準庫 (5)
函數(shù)(4)
類(5)
字符串(2)
關(guān)于 Python 的故事
函數(shù)(3)
字符串(4)
處理股票數(shù)據(jù)
常用數(shù)學函數(shù)和運算優(yōu)先級
字符串(3)
為計算做準備
多態(tài)和封裝
類(4)
迭代
語句(3)
錯誤和異常 (3)
分析 Hello
Python 安裝
標準庫 (2)
列表(2)
元組

將數(shù)據(jù)存入文件

《文件(1)》《文件(2)》中,已經(jīng)學習了如何讀寫文件。

如果在程序中,有數(shù)據(jù)要保存到磁盤中,放到某個文件中是一種不錯的方法。但是,如果像以前那樣存,未免有點凌亂,并且沒有什么良好的存儲格式,導致數(shù)據(jù)以后被讀出來的時候遇到麻煩,特別是不能讓另外的使用者很好地理解。不要忘記了,編程是一個合作的活。還有,存儲的數(shù)據(jù)不一定都是類似字符串、整數(shù)那種基礎(chǔ)類型的。

總而言之,需要將要存儲的對象格式化(或者叫做序列化),才好存好取。這就有點類似集裝箱的作用。

所以,要用到本講中提供的方式。

pickle

pickle 是標準庫中的一個模塊,還有跟它完全一樣的叫做 cpickle,兩者的區(qū)別就是后者更快。所以,下面操作中,不管是用 import pickle,還是用 import cpickle as pickle,在功能上都是一樣的。

>>> import pickle
>>> integers = [1, 2, 3, 4, 5]
>>> f = open("22901.dat", "wb")
>>> pickle.dump(integers, f)
>>> f.close()

pickle.dump(integers, f) 將數(shù)據(jù) integers 保存到了文件 22901.dat 中。如果你要打開這個文件,看里面的內(nèi)容,可能有點失望,但是,它對計算機是友好的。這個步驟,可以稱之為將對象序列化。用到的方法是:

pickle.dump(obj,file[,protocol])

  • obj:序列化對象,上面的例子中是一個列表,它是基本類型,也可以序列化自己定義的類型。
  • file:一般情況下是要寫入的文件。更廣泛地可以理解為為擁有 write() 方法的對象,并且能接受字符串為為參數(shù),所以,它還可以是一個 StringIO 對象,或者其它自定義滿足條件的對象。
  • protocol:可選項。默認為 False(或者說 0),是以 ASCII 格式保存對象;如果設置為 1 或者 True,則以壓縮的二進制格式保存對象。

下面換一種數(shù)據(jù)格式,并且做對比:

>>> import pickle
>>> d = {}
>>> integers = range(9999)
>>> d["i"] = integers        #下面將這個 dict 格式的對象存入文件

>>> f = open("22902.dat", "wb")
>>> pickle.dump(d, f)           #文件中以 ascii 格式保存數(shù)據(jù)
>>> f.close()

>>> f = open("22903.dat", "wb")
>>> pickle.dump(d, f, True)     #文件中以二進制格式保存數(shù)據(jù)
>>> f.close()

>>> import os
>>> s1 = os.stat("22902.dat").st_size    #得到兩個文件的大小
>>> s2 = os.stat("22903.dat").st_size

>>> print "%d, %d, %.2f%%" % (s1, s2, (s2+0.0)/s1*100)
68903, 29774, 43.21%

比較結(jié)果發(fā)現(xiàn),以二進制方式保存的文件比以 ascii 格式保存的文件小很多,前者約是后者的 43%。

所以,在序列化的時候,特別是面對較大對象時,建議將 dump() 的參數(shù) True 設置上,雖然現(xiàn)在存儲設備的價格便宜,但是能省還是省點比較好。

存入文件,僅是一個目標,還有另外一個目標,就是要讀出來,也稱之為反序列化。

>>> integers = pickle.load(open("22901.dat", "rb"))
>>> print integers
[1, 2, 3, 4, 5]

就是前面存入的那個列表。再看看被以二進制存入的那個文件:

>>> f = open("22903.dat", "rb")
>>> d = pickle.load(f)
>>> print d
{'i': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, ....   #省略后面的數(shù)字}
>>> f.close()

還是有自己定義數(shù)據(jù)類型的需要,這種類型是否可以用上述方式存入文件并讀出來呢?看下面的例子:

>>> import cPickle as pickle        #cPickle 更快
>>> import StringIO                 #標準庫中的一個模塊,跟 file 功能類似,只不過是在內(nèi)存中操作“文件”

>>> class Book(object):             #自定義一種類型
...     def __init__(self,name):
...         self.name = name
...     def my_book(self):
...         print "my book is: ", self.name
... 

>>> pybook = Book("<from beginner to master>")
>>> pybook.my_book()
my book is:  <from beginner to master>

>>> file = StringIO.StringIO()
>>> pickle.dump(pybook, file, 1)
>>> print file.getvalue()           #查看“文件”內(nèi)容,注意下面不是亂碼
ccopy_reg
_reconstructor
q(c__main__
Book
qc__builtin__
object
qNtRq}qUnameqU<from beginner to master>sb.

>>> pickle.dump(pybook, file)       #換一種方式,再看內(nèi)容,可以比較一下
>>> print file.getvalue()           #視覺上,兩者就有很大差異
ccopy_reg
_reconstructor
q(c__main__
Book
qc__builtin__
object
qNtRq}qUnameqU<from beginner to master>sb.ccopy_reg
_reconstructor
p1
(c__main__
Book
p2
c__builtin__
object
p3
NtRp4
(dp5
S'name'
p6
S'<from beginner to master>'
p7
sb.

如果要從文件中讀出來:

>>> file.seek(0)       #找到對應類型  
>>> pybook2 = pickle.load(file)
>>> pybook2.my_book()
my book is:  <from beginner to master>
>>> file.close()

shelve

pickle 模塊已經(jīng)表現(xiàn)出它足夠好的一面了。不過,由于數(shù)據(jù)的復雜性,pickle 只能完成一部分工作,在另外更復雜的情況下,它就稍顯麻煩了。于是,又有了 shelve。

shelve 模塊也是標準庫中的。先看一下基本操作:寫入和讀取

>>> import shelve
>>> s = shelve.open("22901.db")
>>> s["name"] = "www.itdiffer.com"
>>> s["lang"] = "python"
>>> s["pages"] = 1000
>>> s["contents"] = {"first":"base knowledge","second":"day day up"}
>>> s.close()

以上完成了數(shù)據(jù)寫入的過程。其實,這更接近數(shù)據(jù)庫的樣式了。下面是讀取。

>>> s = shelve.open("22901.db")
>>> name = s["name"]
>>> print name
www.itdiffer.com
>>> contents = s["contents"]
>>> print contents
{'second': 'day day up', 'first': 'base knowledge'}

當然,也可以用 for 語句來讀:

>>> for k in s:
...     print k, s[k]
... 
contents {'second': 'day day up', 'first': 'base knowledge'}
lang python
pages 1000
name www.itdiffer.com

不管是寫,還是讀,都似乎要簡化了。所建立的對象s,就如同字典一樣,可稱之為類字典對象。所以,可以如同操作字典那樣來操作它。

但是,要小心坑:

>>> f = shelve.open("22901.db")
>>> f["author"]
['qiwsir']
>>> f["author"].append("Hetz")    #試圖增加一個
>>> f["author"]                   #坑就在這里
['qiwsir']
>>> f.close()

當試圖修改一個已有鍵的值時,沒有報錯,但是并沒有修改成功。要填平這個坑,需要這樣做:

>>> f = shelve.open("22901.db", writeback=True)    #多一個參數(shù) True
>>> f["author"].append("Hetz")
>>> f["author"]                   #沒有坑了
['qiwsir', 'Hetz']
>>> f.close()

還用 for 循環(huán)一下:

>>> f = shelve.open("22901.db")
>>> for k,v in f.items():
...     print k,": ",v
... 
contents :  {'second': 'day day up', 'first': 'base knowledge'}
lang :  python
pages :  1000
author :  ['qiwsir', 'Hetz']
name :  www.itdiffer.com

shelve 更像數(shù)據(jù)庫了。

不過,它還不是真正的數(shù)據(jù)庫。真正的數(shù)據(jù)庫在后面。


總目錄   |   上節(jié):第三方庫   |   下節(jié):mysql數(shù)據(jù)庫(1)

如果你認為有必要打賞我,請通過支付寶:qiwsir@126.com,不勝感激。