《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 结合起来使用,会非常高效。

你可能感兴趣的:(【02,Python,基础】)