首页 > 程序开发 > Web开发 > Python >

《Python 编程》笔记(十五)

2016-08-12

本节笔记试图记录一些些在 Python 中使用数据库的方法以及其它数据持久化的技巧。总的来说,本节笔记的针对性强,应该不至于像之前的笔记那样,知识点凌乱不堪。

引言

本节笔记试图记录一些些在 Python 中使用数据库的方法以及其它数据持久化的技巧。总的来说,本节笔记的针对性强,应该不至于像之前的笔记那样,知识点凌乱不堪。

数据和持久化

一些持久化方案:
平面文件(Flat files):直接保存文本和字节; DBM键文件(DBM Keyed Files):按关键字访问存储在字典类文件中的字符串; Pickle 对象(Pickled objects):序列化 Python 对象到文件和流中; Shelve 文件(Shelve files):在DBM键文件中保存 Python pickle 对象; 面向对象的数据库(OODBs):保存 Python 对象到持久化字典中(比如ZODB或Durus); 关系型数据库(RDBMSs):基于表存储数据,支持SQL查询; 对象关系映射(ORMs):映射Python类型到关系型数据库中的表。

DBM 文件

DBM 文件是 Python 库中数据库管理的标准工具之一,实现了数据的随机访问,可以通过键来访问存储的文本字符串。它是 Python 中存储信息最简单的方式之一。 看看如何使用 DBM 文件:
>>> import dbm
>>> file = dbm.open('conf', 'c')
>>> file['user'] = 'user name'
>>> file['password'] = 'password'
>>> file['user']
b'user name'
>>> file.keys()
[b'password', b'user']
>>> len(file)
2
>>> file['website'] = 'http://blog.chriscabin.com'
>>> file['foo'] = 'bar'
>>> file.keys()
[b'website', b'password', b'user', b'foo']
>>> del file['foo']
>>> file.keys()
[b'website', b'password', b'user']
>>> file.close()
>>> file = dbm.open('conf', 'c)
>>> file.keys()
[b'website', b'password', b'user']
>>> for key in file.keys():
    print('{} => {}'.format(key, file[key]))


b'website' => b'http://blog.chriscabin.com'
b'password' => b'password'
b'user' => b'user name'
DBM模块实际上是一个接口规范,并不去管系统具体使用的是什么DBM实现。
当打开一个存在的 DBM 文件时,dbm模块会使用dbm.whichdb函数检测文件使用的是哪种实现创建的,这种检测是基于文件的内容进行的。 当创建一个新文件是时,dbm模块使用固定的顺序来检测系统中存在的基于键的文件接口模块,它会按照dbm.bsd, dbm.gnu, dbm.ndbm, dbm.dumb的顺序来尝试查找这些接口。如果都不存在,则使用最后一个 Python 自带的实现dbm.dumb,当然性能和健全性都不如其它实现。

Pickle 对象

pickle 模块是一种超级通用的数据格式化和去格式化工具,几乎能把内存中任意的 Python 对象转换成字符串,以便存储在无格式的文件中,或者通过网络传输。这种行为叫做序列化。 当一个对象从字节流重建(反序列化)时,它便成为一个和原对象一模一样的对象,拥有相同的数据结构和数据,但是位于不同于原来的内存位置。 由于在 Python 3.x 中,pickle 对象总是 bytes 类型的,而不是 str 类型,所以用来存储 pickle 的 Python 对象文件应该总是以二进制模式打开。

一些情况下是不能 pickle 的:

编译过的代码对象:pickle 中的函数和类只是记录了它们的名字和所在的模块,以便后续使用时重新导入并自动应用模块中的变化; 不可导入的类实例:简短地说,就是在实例被加载时,类必须是可导入的; 用 C 写的,或者和操作系统瞬间状态相关的一些内置和自定义的类型(比如打开的文件对象)。

使用示例:

>>> import pickle
>>> conf = {"user": "user name", "password": "password"}
>>> pickle.dumps(conf)
b'\x80\x03}q\x00(X\x04\x00\x00\x00userq\x01X\t\x00\x00\x00user nameq\x02X\x08\x00\x00\x00passwordq\x03h\x03u.'
>>> pickle.dumps(conf, protocol=0)
b'(dp0\nVuser\np1\nVuser name\np2\nsVpassword\np3\ng3\ns.'
>>> x = pickle.dumps(conf)
>>> pickle.loads(x)
{'user': 'user name', 'password': 'password'}
>>> y = pickle.loads(x)
>>> y == conf, x is conf
(True, False)

Shelve 文件

shelve 是一种可以使用键来存储和检索任意 Python 对象的文件,并且是 Python 原生支持的。它是 DBM 和 pickle 的结合体:

当存储内存中的对象到文件时,shelve 模块会首先使用 pickle 模块将对象序列化,然后使用 DBM 模块根据键将对象字符串存储到 DBM 文件中; 根据键取出对象时,shelve 首先使用 dbm 模块取出键对应的字符串,然后使用pickle 模块将字符串反序列化为原始对象。

shelve 继承了 dbm 的特性,导致它在可移植性上较差!

shelve 模块的简单使用例子,完整的代码参见 shelve_demo
def main():
    database = shelve.open('demo_db.shelve')

    # now, you can save anything to your disk.
    database['stu'] = Student('Chris', 23, 2, 12345)
    database['list'] = [x for x in range(10)]

    # close it
    database.close()

    # reload database
    database = shelve.open('demo_db.shelve')

    # read all saved objects
    for each in database.keys():
        print('{} => {}'.format(each, database[each]))

    # clear all
    database.clear()
    database.close()

# output
list => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
stu => Name: Chris
Age: 23
Grade: 2
Card Id: 12345

shelve 的约束:

键必须是字符串; 单个键对应的对象具有唯一性; 更新:不能采用data[key].attr = value 的方式,而应当采用下面的方式

# 取回

obj = data[key]
obj.attr = value

# 写回去

data[key] = obj
不支持同步更新:实际上是可以同步读取的,但是不能有多个进程同时对一个shelve 文件写入,否则可能会损坏数据。 底层 DBM 格式可能会影响移植性。

面向对象的数据库 ZODB

ZODB (Zope Object Database),它是一个 Python 独有的全功能的面向对象的数据库系统,可以将 ZODB 视作比 shelve 更强大的选择。虽然 ZODB 不支持 SQL,但是ZODB存储的对象可以利用 Python 的全部威力。

ZODB 重要特性:

同步更新: 如果有许多潜在的同步写需求,使用 ZODB 时,无需手动锁定文件防止数据受损。 事务提交和回滚: 除非显式提交,否则做出的改变并不生效。这样即使程序崩溃,也不会造成不一致。 对象自动更新: ZODB 继承自Persistent 超类对象,如果属性变动,则会自动更新。 对象自动缓存: 出于性能的考虑,对象会被缓存在内存中,并在其不需要使用时自动从内存中清除。 平台无关的存储: 由于 ZODB 将数据库存储在独立的扁平文件中,在支持大文件的系统下,可以避免潜在的文件大小限制和 shelve 中 DBM 文件具体实现的差别。

给 Python 3 安装 ZODB:sudo pip3 install ZODB。

下面是使用 ZODB 的模板代码,即创建数据库文件(如果不存在的话),然后连接到数据库,并获取根对象,我们需要在根对象中操作存储,完整的代码参见 zodb_demo
# `FileStorage` 实际上是将一个数据库映射到无格式文本文件的代理对象。
# 使用 ZODB 的一般步骤
self._storage = FileStorage.FileStorage(db_file)
self._db = DB(self._storage)
self._root = self._db.open().root()

# 存储对象
stuff_db.root['foo1'] = 'foo bar hello, world'
stuff_db.root['foo2'] = {'hello': 'world'}
stuff_db.root['foo3'] = [[1, 2, 3, 4], 'hello']

# 事务提交,然后存储到文件中,最后可以关闭数据库
transaction.commit()
self._storage.close()

# 输出结果:
foo2 => {'hello': 'world'}
foo1 => foo bar hello, world
foo3 => [[1, 2, 3, 4], 'hello']
foo2 => {'hello': 'world'}
foo1 => foo bar hello, world
foo3 => [[1, 2, 3, 4], 'hello']
foo2 => {'hello': 'world'}
foo1 => Boy
foo3 => [[1, 2, 3, 4], 'hello']

# 生成的文件:stuff.fs, stuf.fs.index, stuff.fs.lock, stuff.fs.tmp

SQL 数据库接口

SQLite 是Python 自带的关系型数据库。由于 Python 社区定义了通用的数据库操作 API,所以一般可以借助SQLite 做原型开发,后期进行修改后可以轻松地更换成其他的数据库:MySQL, Oracle等。

在数据库 API 中, Python 中的 SQL 数据库基于下面三个概念:

连接对象:代表一个到数据库的连接,是提交和回滚操作的接口,提供数据库软件包的细节信息,生成游标对象; 游标对象:代表了作为字符串提交的 SQL 语句,可以用来访问恩和遍历 SQL 语句的执行结果; 查询 SQL select 语句的结果。

此外,数据库 API 还定义了一套数据库异常类型的标准集合,特殊的数据库类型对象构造器,以及顶级信息查看调用,包括线程安全和风格替换检查。

重要的 API 使用归纳:
使用方式 说明
conn = connect("username/password") 登录数据库服务器,并返回一个连接对象
conn.close() 关闭数据库连接
conn.commit() 提交事务
conn.rollback() 回滚未决定的事务
cursor = conn.cursor() 获取游标对象,这样你就可以执行 SQL 了。
cursor.execute(sqlstr, [, params]) 执行 SQL 语句。运行后,游标的rowcount属性返回更改的行数等。description首先可以记录表中的字段名称和字段属性。
cursor.fetchone(), cursor.fetchmany([size]), cursor.fetchall() 获取查询结果

- 演示使用 Python 数据库 API 的基本方法示例参见 sqlite_demo.py。在这个示例中,简要介绍了以下几种操作(主要还是需要熟悉 SQL):
- 与sqlite数据库连接,并创建数据库表;
- 演示如何插入记录的三种方法;
- 如何进行查询操作;
- 如何进行记录的更新;
- 如何删除记录。
- 查询后的结果是一个元组,不方便我们使用。所以,一个比较简单的方法是,将返回的结果转换成字典,详细的代码示例参见 sqlite_advance.py

# 获取列名
col_names = [x[0] for x in cursor.description]

# 获取一行
row = cursor.fetchone()

# 打包成字典
dict_result = dict(zip(col_names, row))
将 Python 语言和 SQL 结合起来使用,会非常高效。
相关文章
最新文章
热点推荐