Python中的对象序列化

本文介绍了Python中的对象序列化,包括定义、目的和使用pickle模块进行对象持久化的方法。序列化用于将对象状态转化为可存储或传输的形式,适用于对象持久化、数据传输等场景。在Python中,pickle模块提供了序列化和反序列化功能,但要注意不可序列化的对象类型,如文件对象,以及类实例的序列化特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、什么是对象序列化

    序列化(serialization)指的是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。

    通常,对象实例的所有字段都会被序列化,这意味着数据会被表示为实例的序列化数据。这样,能够解释该格式的代码有可能能够确定这些数据的值,而不依赖于该成员的可访问性。类似地,反序列化从序列化的表示形式中提取数据,并直接设置对象状态,这也与可访问性规则无关。

2、序列化的目的

    1、以某种存储形式使自定义对象持久化;  

    2、将对象从一个地方传递到另一个地方; 

    3、使程序更具维护性。

3、Python中的对象序列化

    对于对象持久化而言,可以将对象数据存储在某种格式的文本文件中,譬如 CSV 文件。或者可以用关系数据库,譬如MySQL、PostgreSQL 或者 DB2。对于所有这些存储机制,Python 都有健壮的接口。这些存储机制存储的数据是独立于对这些数据进行操作的对象和程序的。这样做的好处是:数据可以作为共享的资源,供其它应用程序使用。缺点是:用这种方式,可以允许其它程序访问对象的数据。这违背了面向对象的封装性原则 — 即对象的数据只能通过这个对象自身的公共(public)接口来访问。另外,对于某些应用程序,关系数据库方法不是很理想。关系数据库并不理解对象,并且会强行使用自己的类型系统和关系数据模型(表),每张表包含一组元组(行),每行包含具有固定数目的静态类型字段(列)。如果应用程序的对象模型不能够方便地转换到关系模型,那么在将对象映射到元组以及将元组映射回对象方面,会碰到一定难度。这种困难常被称为阻碍性不匹配(impedance-mismatch)问题。

    Object Model(对象模型)和RelationalModel(关系模型)不能匹配具体表现在以下几个方面:

   1.The problem of Granularity
    在对象模型中一个User类有一个Address类属性;在关系模型中User表没有对应Address的一个列,而是有city,street,zipcode等列联合表示Address。因此粒度不匹配。
   2.The problem of subtypes
    对象模型中的继承和多态在关系模型中没有对应的实现。
   3.The problem of identity
    对象模型比如java语言有“==”和“equals”两种判断对象相等的方法,而关系模型是通过主键判断的。关系模型中的无意义主键在对象模型中可能是不存在的,比如User主键userId在User类中并不存在。
   4.Problems relating to associations
    对象模型中的相互包含集合(User中有bill的Set,Bill中有user的Set),是一个多对多的关系,这个关系对应到关系模型中必须新建一个表来表示,而这个表在对象模型中没有类与之对应。
   5.The problem of data navigation
    在对象模型中数据导航访问是通过网状的对象引用进行的,如果在关系模型中通过网状的表引用来进行数据导航访问将导致"n+1 select"问题,降低效率。

    在 Python 中,解决序列化问题需要利用 pickle,可以将对象 pickle 成字符串、磁盘上的文件或者任何类似于文件的对象,也可以将这些字符串、文件或任何类似于文件的对象 unpickle 成原来的对象。
    基本用法:

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

这是将对象持久化的方法,参数的含义分别为:
obj: 要持久化保存的对象;
file: 一个拥有 write() 方法的对象,并且这个 write() 方法能接收一个字符串作为参数。这个对象可以是一个以写模式打开的文件对象或者一个 StringIO 对象,或者其他自定义的满足条件的对象。
protocol: 这是一个可选的参数,默认为 0 ,如果设置为 1 或 True,则以高压缩的二进制格式保存持久化后的对象,否则以ASCII格式保存。

pickle.load(file)

只有一个参数 file ,对应于上面 dump 方法中的 file 参数。这个 file 必须是一个拥有一个能接收一个整数为参数的 read() 方法以及一个不接收任何参数的 readline() 方法,并且这两个方法的返回值都应该是字符串。这可以是一个打开为读的文件对象、StringIO 对象或其他任何满足条件的对象。

    可以被pickle 的东西:
  • 所有Python支持的 原生类型 : 布尔, 整数, 浮点数, 复数, 字符串, bytes(字节串)对象, 字节数组, 以及 None.
  • 由任何原生类型组成的列表,元组,字典和集合。
  • 由任何原生类型组成的列表,元组,字典和集合组成的列表,元组,字典和集合(可以一直嵌套下去,直至Python支持的最大递归层数).
  • 函数,类,和类的实例(带警告)。
    如果这还不够用, pickle模块也是可扩展的。cPickle 向 Python 也提供了 pickle 支持。 并且是用 C 编码的,具有更好的性能,对于大多数应用程序,推荐使用该模块 (Python3中,这两个模块已经合并,直接 import pickle 就可以)。

4、pickle使用的一些问题

pickle是可移植的

pickle 是可移植的。pickle 文件格式独立于机器的体系结构,这意味着,可以在 Linux 下创建一个 pickle,然后将它发送到在 Windows 或 Mac OS 下运行的 Python 程序。并且,当升级到更新版本的 Python 时,不必担心可能要废弃已有的 pickle。Python 开发人员已经保证 pickle 格式将可以向后兼容 Python 各个版本。事实上,在 pickle 模块中提供了有关目前以及所支持的格式方面的详细信息:
检索所支持的格式:
>>> import pickle
>>> pickle.format_version
'2.0'
>>> pickle.compatible_formats
['1.0', '1.1', '1.2', '1.3', '2.0']

多个引用,同一对象

在 Python 中,变量是对象的引用,可以用多个变量引用同一个对象。Python 在用经过 pickle 的对象维护这种行为方面丝毫没有困难。

>>> a = [1, 2]
>>> b = a
>>> a.append(3)
>>> a
[1, 2, 3]
>>> b
[1, 2, 3]
>>> c = pickle.dumps((a, b))
>>>
>>> d, e = pickle.loads(c)
>>> d
[1, 2, 3]
>>> e
[1, 2, 3]
>>> d.append(4)
>>> d
[1, 2, 3, 4]
>>> e
[1, 2, 3, 4]

不可 pickle 的对象

一些对象类型是不可 pickle 的。例如,Python 不能 pickle 文件对象(或者任何带有对文件对象引用的对象),因为 Python 在 unpickle 时不能保证它可以重建该文件的状态。

>>> f = open('out.txt')
>>> p = pickle.dumps(f)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/pickle.py", line 1374, in dumps
    Pickler(file, protocol).dump(obj)
  File "/usr/lib/python2.7/pickle.py", line 224, in dump
    self.save(obj)
  File "/usr/lib/python2.7/pickle.py", line 306, in save
    rv = reduce(self.proto)
  File "/usr/lib/python2.7/copy_reg.py", line 70, in _reduce_ex
    raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle file objects

类实例

由于 Python 会 pickle 实例数据(通常是 _dict_ 属性)和类的名称,而不会 pickle 类的代码。当 Python unpickle 类的实例时,它会试图使用在 pickle 该实例时的确切的类名称和模块名称(包括任何包的路径前缀)导入包含该类定义的模块。类定义必须出现在模块的最顶层,它们不能是嵌套的类(在其它类或函数中定义的类)。
当 unpickle 类的实例时,通常不会再调用它们的 _init_() 方法。相反,Python 创建一个通用类实例,并应用已进行过 pickle 的实例属性,同时设置该实例的 _class_ 属性,使其指向原来的类。



5、参考

    51CTO:LINUX应用技巧,序列化存储 Python 对象。http://developer.51cto.com/art/200509/3892.htm





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值