如何在 Python 中实现持久对象
在编程中,常常需要将数据保存在磁盘上,以便下次程序运行时提取数据并使用。这就需要使用持久对象,即可以在多次程序执行之间保持状态的对象。Python 为我们提供了多种方式来实现持久对象,本文就介绍其中的两种:pickle 和 shelve。
Pickle
pickle 是 Python 标准库中的一个模块,它可以将 Python 对象序列化成字节流,以便可以将其写入磁盘或发送到网络。反之,也可以将字节流反序列化成 Python 对象。pickle 不仅支持所谓的“普通”对象(数字、字符串、列表等),而且还可以对自定义类进行序列化和反序列化。
序列化
让我们来看一个小例子,假设我们有一个名为 Person 的类,其中有两个属性:姓名和年龄:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
我们创建一个 Person 对象并对其进行序列化:
import pickle
person = Person('Tom', 20)
with open('person.pkl', 'wb') as f:
pickle.dump(person, f)
这样,我们就将 person 对象序列化到了一个名为 person.pkl 的文件中。下面我们来看看 person.pkl 文件:
这看起来不是很直观,事实上,pickle 默认是将对象以二进制格式存储到文件中的。如果我们想要将这些内容以字符串形式显示,可以使用 pickle.dumps()
方法:
import pickle
person = Person('Tom', 20)
person_str = pickle.dumps(person)
print(person_str)
输出:
b'\x80\x04\x95?\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x06Person\x94\x93\x94\x8c\x04name\x94\x8c\x03Tom\x94\x8c\x03age\x94K\x14\x86\x94.'
反序列化
我们现在有一个名为 person.pkl 的文件,想要将其反序列化为 Python 对象,可以使用 pickle.load()
方法:
import pickle
with open('person.pkl', 'rb') as f:
person = pickle.load(f)
print(person.name, person.age)
输出:
Tom 20
注意事项
pickle 在序列化 Python 对象时有一些限制。以下是一些最常见的:
- 无法序列化 lambda、函数和方法。
- 无法序列化实例方法(由于它们包含对实例的引用)。
- 无法在不同计算机之间共享 pickle(因为 pickle 包含了 Python 对象的完整表示,而不是二进制数据)。
- 序列化和反序列化都需要使用相同的 pickle 版本(否则可能会出现错误或不兼容性)。
- pickle 可能存在安全风险(例如,当加载 pickle 文件时,如果不信任文件源,则 pickle 可能会执行恶意代码)。
Shelve
shelve 模块是 Python 标准库中的一个键值存储模块,顾名思义,它的作用类似于书架:将一系列“书籍”存储在其中,并通过“书名”来查找和获取它们。shelve 可以存储 Python 对象,并将其序列化为字节流,以便可以将其写入磁盘或发送到网络。与 pickle 不同,shelve 存储的是一个个对象,而不是一个个字节流。
存储
让我们来看一个小例子,假设我们有一个名为 Person 的类(与 pickle 中的相同),我们可以使用 shelve 存储多个 Person 对象:
import shelve
with shelve.open('persons') as db:
db['person1'] = Person('Tom', 20)
db['person2'] = Person('Jerry', 30)
这里我们使用了 shelve.open()
方法来打开一个名为 persons 的数据库,并使用 db['person1']
和 db['person2']
来存储两个 Person 对象。保存在磁盘上的数据类似于一个字典,每一个键值对就是一个对象。
获取
我们现在已经将多个 Person 对象存储在了数据库中,我们可以通过键名来获取它们:
import shelve
with shelve.open('persons') as db:
person1 = db['person1']
person2 = db['person2']
print(person1.name, person1.age)
print(person2.name, person2.age)
输出:
Tom 20
Jerry 30
注意事项
与 pickle 一样,shelve 存在一些限制和安全问题。以下是一些最常见的:
- 无法在不同计算机之间共享 shelve(因为它们使用了 Python 内部的数据结构)。
- 在使用 shelve 时需要注意多线程安全(即需要保证多个线程访问 shelve 数据时没有冲突)。
- shelve 相比 pickle 明显更慢,因为它需要将 Python 对象转换为字节流和反转换回 Python 对象。
结论
本文介绍了如何在 Python 中使用 pickle 和 shelve 实现持久对象。pickle 可以将 Python 对象序列化为字节流并保存到磁盘上,反之亦然。而 shelve 则是一个键值存储模块,可以存储多个 Python 对象,并通过键名来访问它们。当然,我们也需要注意到它们的限制和安全问题,以便在使用它们的时候避免出现错误。