12. 数据持久化

12.1. pickle

提供二进制级别的序列化和反序列化。

和json比较

  • pickle协议和JSON(JavaScript Object Notation)之间有着根本的区别:

  • JSON是一种文本序列化格式(它输出unicode文本,虽然大多数时候它被编码为utf-8),而pickle是一种二进制序列化格式。

  • JSON是人类可读的,而pickle不是;

  • JSON可以在Python生态系统之外互操作并广泛使用,而pickle则是Python特有的;

  • 默认情况下,JSON只能表示Python内置类型的一个子集,并且不包含自定义类; pickle可以代表大量的Python类型。

序列化和反序列号样例:

# 使用dump来将python对象直接dump到文件中去

import pickle

# 构造一个python对象
data = {
    'a': [1, 2.0, 3, 4+6j],
    'b': ("character string", b"byte string"),
    'c': {None, True, False}
}
with open('data.pickle', 'wb') as f:
    pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)

# 使用load来将文件直接load成python对象
import pickle
with open('data.pickle', 'rb') as f:
    # 这个地方就不能在指定版本了,会自动识别的,指定了反而不好。
    data = pickle.load(f)

load接受的是文件描述符,loads接受的是文件的内容,dump接受文件描述符,dumps默认返回str

12.2. copyreg

copyreg模块提供了一种自定义函数来序列和反序列特定对象时使用的函数的机制。

In [13]: import copyreg,copy,pickle

In [14]: class C(object):
    ...:     def __init__(self,a):
    ...:         self.a=a
    ...:

In [15]: def pickle_c(c):
    ...:     print("picking c instance...")
    ...:     return C,(c.a,)
    ...:

In [16]: copyreg.pickle(C,pickle_c)

In [17]: c=C(1)

In [18]: d=copy.copy(c)
picking c instance...

In [19]: p= pickle.dumps(c)
picking c instance...

In [20]: p
Out[20]: b'\x80\x03c__main__\nC\nq\x00K\x01\x85q\x01Rq\x02.'

12.3. shelve

shelve是一个持久的,类似字典的对象。

In [21]: import shelve

# 打开一个文件,可以不存在,会创建的
In [22]: d=shelve.open("a.shelve")

# 设置一些属性值
In [23]: d["name"]="zhaojiedi"

In [24]: d["age"]= 26

# 列出keys
In [26]: list(d.keys())
Out[26]: ['name', 'age']

# 关闭
In [27]: d.close()

# 再次打开
In [28]: d=shelve.open("a.shelve")

# 还是可以获取到原有的数据的
In [29]: d["name"]
Out[29]: 'zhaojiedi'

12.4. marshal

该模块包含可以以二进制格式读取和写入Python值的函数。

和pickle用法差不多,重要是load,loads,dump,dumps这4个方法。

简单序列化和反序化样例:

In [30]: import marshal

In [31]: d=[1,2,3,4]

In [33]: e = marshal.dumps(d)

In [34]: e
Out[34]: b'\xdb\x04\x00\x00\x00\xe9\x01\x00\x00\x00\xe9\x02\x00\x00\x00\xe9\x03\x00\x00\x00\xe9\x04\x00\x00\x00'

In [35]: f = marshal.loads(e)

In [36]: f
Out[36]: [1, 2, 3, 4]

In [37]: id(f) == id(d)
Out[37]: False

12.5. dbm

dbm是DBM数据库变体的通用接口,dbm.gnu或dbm.ndbm。 如果没有安装这些模块, 则将使用模块dbm.dumb中的慢而简单的实现。

In [40]: import dbm
# r只读,w写,c读写,且如果不存在创建,n创建一个新的,如果存在就读写打开。
In [41]: with dbm.open('cache','c') as db:
    ...:     db["name"]="zhaojiedi"
    ...:     db["age"]="26"
    ...:     print(db.get('name'))
    ...:     db["weight"]="100"

这个和marshal一样,这次修改完毕,下次再次打开还是可以得到上次的数据的。

Warning

使用dbm的时候value只能是bytes 或者 strings。

12.6. sqllite

SQLite是一个C库,它提供了一个轻量级的基于磁盘的数据库,它不需要单独的服务器进程,并允许 使用SQL查询语言的非标准变体访问数据库。 一些应用程序可以使用SQLite进行内部数据存储。 也可以使用SQLite对应用程序进行原型设计,然后将代码移植到更大的数据库,如PostgreSQL或Oracle。

这个模块使用起来像我们使用大型数据一样,只是这个数据库比较low而已,没有进程和复杂的数据库管理功能。

样例:

# 导入
In [2]: import sqlite3

# 创建一个连接,example.db可以存在,可以不存在
In [3]: conn = sqlite3.connect("example.db")

# 获取cursor
In [4]: c = conn.cursor()

# 执行创建表语句
In [5]: c.execute('''CREATE TABLE stocks
...:              (date text, trans text, symbol text, qty real, price real)''')
Out[5]: <sqlite3.Cursor at 0x1d2ea658650>

# 插入单条数据
In [6]: c.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")
Out[6]: <sqlite3.Cursor at 0x1d2ea658650>

# 批量插入
In [11]: example_date = [('2007-01-05','BUY','RHAT',100,35.14),('2008-01-05','BUY','RHAT',100,35.14)]

In [12]: c.executemany('insert into stocks values(?,?,?,?,?)',example_data)

# 提交到数据库
In [7]: conn.commit()

# 查询,遍历cursor
In [16]: c.execute("select * from stocks")
Out[16]: <sqlite3.Cursor at 0x1d2ea6588f0>

In [17]: for row in c:
    ...:     print(row)
    ...:
('2006-01-05', 'BUY', 'RHAT', 100.0, 35.14)
('2007-01-05', 'BUY', 'RHAT', 100.0, 35.14)
('2008-01-05', 'BUY', 'RHAT', 100.0, 35.14)
# 查询后直接获取所有结果
In [19]: c.execute("select * from stocks")
Out[19]: <sqlite3.Cursor at 0x1d2ea6588f0>
In [20]: c.fetchall()
Out[20]:
[('2006-01-05', 'BUY', 'RHAT', 100.0, 35.14),
('2007-01-05', 'BUY', 'RHAT', 100.0, 35.14),
('2008-01-05', 'BUY', 'RHAT', 100.0, 35.14)]

# 查询,一个一个查询
In [21]: c.execute("select * from stocks")
Out[21]: <sqlite3.Cursor at 0x1d2ea6588f0>

In [22]: c.fetchone()
Out[22]: ('2006-01-05', 'BUY', 'RHAT', 100.0, 35.14)

In [23]: c.fetchone()
Out[23]: ('2007-01-05', 'BUY', 'RHAT', 100.0, 35.14)

In [24]: c.fetchone()
Out[24]: ('2008-01-05', 'BUY', 'RHAT', 100.0, 35.14)

In [25]: c.fetchone()

# 特定条件查询

In [29]: c.execute("select * from stocks where date=:date and trans=:trans",{"date":'2006-01-05',"trans":'BUY'})
Out[29]: <sqlite3.Cursor at 0x1d2ea6588f0>

In [30]: c.fetchall()
Out[30]: [('2006-01-05', 'BUY', 'RHAT', 100.0, 35.14)]

# 关闭连接
In [8]: conn.close()