序列化Python对象——pickle模块的基本使用

当我们要在多台计算机之间进行数据交换、传输Python对象的时候,可以使用诸如UNIX的管道或者socket套接字。但是在传输之前,我们首先要做的就是要将对象序列化。

序列化的作用:

  • 将对象保存在文件中

  • 将对象保存在数据库中

  • 通过网络进行传输

在python的编程世界中,最常见的序列化和反序列化方法便是使用pickle模块。


基本的序列化,使用pickle.dump()和pickle.dumps()函数:

1>将某个对象存储在文件中

>>> import pickle
>>> data = 'Amos H'
>>> f = open('somefile', 'wb')
>>> pickle.dump(data, f)

2>将某个对象存储在字符串中

>>> s = pickle.dumps(data)

基本的反序列化,可以使用pickle.load()从文件中重建对象,也可以使用pickle.loads()函数从字符串中重建对象。

>>> f.close()
>>> f = open('somefile', 'rb')
>>> data  = pickle.load(f)
>>> data
'Amos H'
>>> data = pickle.loads(s)
>>> data
'Amos H'

pickle模块能够兼容大部分Python数据类型和用户自定义的类实例。如果正在使用的库可以保存或回复Python对象到数据库中,或者通过网络传输对象,那么很有可能就在使用pickle模块。


pickle是一种Python专有的自描述式的数据编码。

说到自描述,因为序列化的数据中包含有每个对象的开始和结束以及有关对象类型的信息。因此,不需要担心应该如何定义记录——pickle就能自动完成了。

如果需要处理多个对象,可以这么做:

>>> import pickle
>>> f = open('somedata', 'wb')
>>> pickle.dump([1,2,3,4],f)
>>> pickle.dump('Amos H', f)
>>> pickle.dump(1.234567, f)
>>> f.close()
>>> f = open('somedata', 'rb')
>>> pickle.load(f)
[1, 2, 3, 4]
>>> pickle.load(f)
'Amos H'
>>> pickle.load(f)
1.234567

当对数据做反序列化处理时,会假设所需的源文件都是可用的。模块、类以及函数会根据需要自动导入。

对于需要在不同机器上的解释器之间共享Python数据的应用,这会成为一个潜在的维护性问题,因为所有的机器都必须能够访问到相同的源代码。

注意:

绝对不能对非受信任的数据使用pickle.load()。因为pickle会自动加载模块并创建实例。

了解pickle是如何运作的骇客可以故意创建出格式不正确的数据,使得Python解释器有机会去执行任意的系统命令。

因此,有必要将pickle限制为只在内部使用,解释器和数据之间要能够彼此验证对方。


某些特定类型的对象是无法进行pickle操作的。这些对象一般来说是涉及某种外部系统状态,比如打开文件、打开数据链接、线程、进程、帧栈等。

用户自定义的类有时候可以通过提供__getstate__()和__setstate__()方法来规避这些限制。

如果定义了这些方法,pickle.dump()就会调用__getstate__()来得到一个可以被pickle处理的对象。同样的,在unpickle的时候就会调用__setstate__()。

接下来使用一个Countdown类来进行演示:

#countdown.py
import time
import threading

class Countdown:
    def __init__(self, n):
        self.n = n
        self.thr = threading.Thread(target=self.run)
        self.thr.daemon = True
        self.thr.start()
        
    def run(self):
        while self.n > 0:
            print('T-minus', self.n)
            self.n -= 1
            time.sleep(5)

    def __getstate__(self):
        return self.n

    def __setstate__(self, n):
        self.__init(n)

接下来来体验一下pickle的操作:

>>> import countdown
>>> c = countdown.Countdown(30)
>>> T-minus 30
T-minus 29
T-minus 28
T-minus 27
....

>>> f = open('cstate.p','wb')
>>> import pickle
>>> pickle.dump(c,f)
>>> f.close()

运行一段时间之后,关闭解释器,再重启解释器试试这个:

>>> import pickle
>>> f = open('cstate.p', 'rb')
>>> pickle.load(f)
<countdown.Countdown object at 0x0000000002B59828>
T-minus 26
T-minus 25
T-minus 24
T-minus 23
T-minus 22
T-minus 21
...

可以看到线程又奇迹般的重新换发生命了,而且是从上次执行pickle操作时剩下的技术开始执行的。

对于大型的数据结构,比如array模块或者numpy库创建的二进制数组,pickle就不是一种高效的编码了。如果需要移动大量的数组性数据,那么最好简单的将数据按块保存在文件中,或者使用更加标准的编码。

由于pickle是Python的专用特性,而且同源代码的关联紧密,因此不应该把pickle作为长期存储的格式。如果要将数据保存在数据库和归档存储中,最好使用一种更加标准的数据编码,比如XML、CSV或者JSON。这些编码方式的标准化程度更高,许多变成语言都支持,而且更能适应于源代码的修改。

最后但同样重要的是,请注意pickle模块中有着大量的选项以及棘手的阴暗角落。对于大部分常见的用途,我们不需要担心这些问题。但是要构建一个大型的应用,其中要用pickle来做序列化的话,应该好好参考官方文档。

更多技术干货,有趣文章,点这里没错

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值