用 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'}
>>>
>>>
请记住,当您释放数据时,对象会浮现,因此切勿尝试处理来自不受信任来源的转储数据。 恶意用户可以使用这种技术在系统上执行任意命令。