7.6 KiB
用 Python 转储对象
于 2020 年 1 月 7 日更新
在用 Python 阅读和编写 JSON 一文中,我们了解了如何在 Python 中处理 JSON 数据。 如果您还没有看完这篇文章,我建议您这样做,然后再回到这里。
事实证明,json模块不是序列化数据的唯一方法。 Python 提供了另一个名为pickle的模块来对数据进行序列化和反序列化。
这是json和pickle模块之间的主要区别。
-
pickle模块是特定于 Python 的,这意味着对象被序列化后,就不能使用其他语言(如 PHP,Java,Perl 等)反序列化。如果需要互操作性,则请坚持使用json模块。 -
与
json模块将对象序列化为人类可读的 JSON 字符串不同,pickle模块以二进制格式序列化数据。 -
json模块允许我们仅序列化基本的 Python 类型(例如int,str,dict,list等)。 如果需要序列化自定义对象,则必须提供自己的序列化函数。 但是,pickle模块可立即使用多种 Python 类型,包括您定义的自定义对象。 -
pickle模块的大多数代码都是用 C 编码的。因此,与json模块相比,它在处理大型数据集时性能得到了极大的提高。
pickle模块提供的接口与json模块相同,并且由dump() / load()和dumps() / loads()函数组成。
要使用pickle模块,请按以下步骤导入它:
>>>
>>> import pickle
>>>
现在让我们看看如何使用pickle模块来序列化和反序列化对象。
注意:
序列化和反序列化有时也分别称为转储和加载。
用dump()转储
转储数据通过dump()函数完成。 它接受数据和文件对象。 然后,dump()函数将数据序列化并将其写入文件。 dump()的语法如下:
语法:dump(obj, file)
| 参数 | 描述 |
|---|---|
obj |
要转储的对象。 |
file |
将写入转储数据的文件对象。 |
这是一个例子:
>>>
>>> import pickle
>>>
>>> from datetime import datetime
>>>
>>>
>>> f = open("my_pickle", "wb") # remember to open the file in binary mode
>>>
>>> pickle.dump(10, f)
>>> pickle.dump("a string", f)
>>> pickle.dump({'a': 1, 'b': 2}, f)
>>> pickle.dump(datetime.now(), f) # serialize datetime.datetime object
>>>
>>> f.close()
>>>
>>>
这里有两件事要注意:
- 首先,我们以二进制模式而不是文本模式打开文件。 这是必要的,否则在写入时数据将被破坏。
- 其次,
dump()函数能够对datetime.datetime对象进行序列化,而无需提供任何自定义序列化函数。
显然,我们不仅限于datetime.datetime对象。 举一个例子,以下清单对 Python 中可用的其他一些类型进行了序列化。
>>>
>>> class My_class:
... def __init__(self, name):
... self.name = name
...
>>>
>>>
>>> def func(): return "func() called"
...
>>>
>>>
>>> f = open("other_pickles", "wb")
>>>
>>> pickle.dump(My_class, f) # serialize class object
>>>
>>> pickle.dump(2 + 3j, f) # serialize complex number
>>>
>>> pickle.dump(func, f) # serialize function object
>>>
>>> pickle.dump(bytes([1, 2, 3, 4, 5]), f) # serialize bytes object
>>>
>>> pickle.dump(My_class("name"), f) # serialize class instance
>>>
>>> f.close()
>>>
>>>
我们现在转储了一些数据。 此时,如果您尝试从文件中读取数据,则会将数据作为bytes对象获得。
>>>
>>> open("my_pickle", "rb").read()
b'\x80\x03K\n.\x80\x03X\x08\x00\x00\x00a stringq\x00.\x80\x03}q\x00(X\x01\x00\x00\x00bq\x01K\x02X\x01\x00\x00\x00aq\x02K\x01u.\x80\x03cdatetime\ndatetime\nq\x00C\n\x07\xe2\t\x1e\x10.\x1e\r9\x92q\x01\x85q\x02Rq\x03.'
>>>
>>>
>>> open("other_pickles", "rb").read()
b'\x80\x03c__main__\nMy_Class\nq\x00.\x80\x03cbuiltins\ncomplex\nq\x00G@\x00\x00\x00\x00\x00\x00\x00G@\x08\x00\x00\x00\x00\x00\x00\x86q\x01Rq\x02.\x80\x03c__main__\nfunc\nq\x00.\x80\x03C\x05\x01\x02\x03\x04\x05q\x00.\x80\x03c__main__\nMy_Class\nq\x00)\x81q\x01}q\x02X\x04\x00\x00\x00nameq\x03h\x03sb.'
>>>
>>>
这不是很可读。 对?
要恢复拾取的对象,我们使用load()函数
用load()加载
load()函数获取一个文件对象,从转储的表示中重建对象,然后将其返回。
其语法如下:
| 参数 | 描述 |
|---|---|
file |
从中读取序列化数据的文件对象。 |
现在,让我们尝试阅读我们在本文前面创建的my_pickle文件。
>>>
>>> f = open("my_pickle", "rb")
>>>
>>> pickle.load(f)
10
>>> pickle.load(f)
'a string'
>>>
>>> pickle.load(f)
{'b': 2, 'a': 1}
>>>
>>> pickle.load(f)
datetime.datetime(2018, 9, 30, 16, 46, 30, 866706)
>>>
>>> pickle.load(f)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
EOFError: Ran out of input
>>>
>>> f.close()
>>>
注意,对象的返回顺序与我们首先对其进行转储的顺序相同。 另外,请注意,该文件以二进制模式打开以进行读取。 当没有更多数据要返回时,load()函数将引发EOFError。
同样,我们可以从other_pickles文件中读取转储的数据。
>>>
>>>
>>> f = open("other_pickles", "rb") # open the file for reading in binary mode
>>>
>>> My_class = pickle.load(f)
<class '__main__.My_class'>
>>>
>>>
>>> c = pickle.load(f)
>>>
>>> c
(2+3j)
>>>
>>>
>>> func = pickle.load(f)
>>>
>>> func
<function func at 0x7f9aa6ab6488>
>>>
>>>
>>> b = pickle.load(f)
>>>
>>> b
b'\x01\x02\x03\x04\x05'
>>>
>>>
>>> my_class_obj = pickle.load(f)
>>> my_class_obj
<__main__.My_Class object at 0x7f9aa74e61d0>
>>>
>>>
>>> pickle.load(f)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
EOFError: Ran out of input
>>>
>>>
>>> f.close()
>>>
>>>
加载数据后,就可以像普通的 Python 对象一样使用它了。
>>>
>>> func()
'func() called'
>>>
>>>
>>> c.imag, c.real
(3.0, 2.0)
>>>
>>>
>>> My_class("Tom")
<__main__.My_Class object at 0x7f9aa74e6358>
>>>
>>>
>>> my_class_obj.name
'name'
>>>
>>>
使用dumps()和load()进行转储和加载
dumps()的工作方式与dump()完全相同,但是它不是将输出发送到文件,而是将转储的数据作为字符串返回。 其语法如下:
语法:dumps(obj) -> pickled_data
| 参数 | 描述 |
|---|---|
obj |
要序列化的对象 |
同样,loads()函数与load()相同,但是它不是从文件中读取转储的数据,而是从字符串中读取数据。 其语法如下:
语法:loads(pickled_data) -> obj
| 参数 | 描述 |
|---|---|
pickled_data |
转储数据 |
Here is an example:
>>>
>>> employee = {
... "first_name": "Mike",
... "designation": 'Manager',
... "doj": datetime(year=2016, month=5, day=2), # date of joining
... }
>>>
>>>
>>> pickled_emp = pickle.dumps(employee) # pickle employee dictionary
>>>
>>> pickled_emp
b'\x80\x03}q\x00(X\x0b\x00\x00\x00designationq\x01X\x07\x00\x00\x00Managerq\x02X\x03\x00\x00\x00dojq\x03cdatetime\ndatetime\nq\x04C\n\x07\xe0\x05\x02\x00\x00\x00\x00\x00\x00q\x05\x85q\x06Rq\x07X\n\x00\x00\x00first_nameq\x08X\x04\x00\x00\x00Mikeq\tu.'
>>>
>>>
>>> pickle.loads(pickled_emp) # unpickle employee dictionary
{'designation': 'Manager', 'doj': datetime.datetime(2016, 5, 2, 0, 0), 'first_name': 'Mike'}
>>>
>>>
请记住,当您释放数据时,对象会浮现,因此切勿尝试处理来自不受信任来源的转储数据。 恶意用户可以使用这种技术在系统上执行任意命令。