用 Python 转储对象

原文: https://thepythonguru.com/pickling-objects-in-python/


于 2020 年 1 月 7 日更新


用 Python 阅读和编写 JSON 一文中,我们了解了如何在 Python 中处理 JSON 数据。 如果您还没有看完这篇文章,我建议您这样做,然后再回到这里。

事实证明,json模块不是序列化数据的唯一方法。 Python 提供了另一个名为pickle的模块来对数据进行序列化和反序列化。

这是jsonpickle模块之间的主要区别。

  1. pickle模块是特定于 Python 的,这意味着对象被序列化后,就不能使用其他语言(如 PHP,Java,Perl 等)反序列化。如果需要互操作性,则请坚持使用json模块。

  2. json模块将对象序列化为人类可读的 JSON 字符串不同,pickle模块以二进制格式序列化数据。

  3. json模块允许我们仅序列化基本的 Python 类型(例如intstrdictlist等)。 如果需要序列化自定义对象,则必须提供自己的序列化函数。 但是,pickle模块可立即使用多种 Python 类型,包括您定义的自定义对象。

  4. pickle模块的大多数代码都是用 C 编码的。因此,与json模块相比,它在处理大型数据集时性能得到了极大的提高。

pickle模块提供的接口与json模块相同,并且由dump() / load()dumps() / loads()函数组成。

要使用pickle模块,请按以下步骤导入它:

  1. >>>
  2. >>> import pickle
  3. >>>

现在让我们看看如何使用pickle模块来序列化和反序列化对象。

注意

序列化反序列化有时也分别称为转储加载

dump()转储


转储数据通过dump()函数完成。 它接受数据和文件对象。 然后,dump()函数将数据序列化并将其写入文件。 dump()的语法如下:

语法dump(obj, file)

参数 描述
obj 要转储的对象。
file 将写入转储数据的文件对象。

这是一个例子:

  1. >>>
  2. >>> import pickle
  3. >>>
  4. >>> from datetime import datetime
  5. >>>
  6. >>>
  7. >>> f = open("my_pickle", "wb") # remember to open the file in binary mode
  8. >>>
  9. >>> pickle.dump(10, f)
  10. >>> pickle.dump("a string", f)
  11. >>> pickle.dump({'a': 1, 'b': 2}, f)
  12. >>> pickle.dump(datetime.now(), f) # serialize datetime.datetime object
  13. >>>
  14. >>> f.close()
  15. >>>
  16. >>>

这里有两件事要注意:

  1. 首先,我们以二进制模式而不是文本模式打开文件。 这是必要的,否则在写入时数据将被破坏。
  2. 其次,dump()函数能够对datetime.datetime对象进行序列化,而无需提供任何自定义序列化函数。

显然,我们不仅限于datetime.datetime对象。 举一个例子,以下清单对 Python 中可用的其他一些类型进行了序列化。

  1. >>>
  2. >>> class My_class:
  3. ... def __init__(self, name):
  4. ... self.name = name
  5. ...
  6. >>>
  7. >>>
  8. >>> def func(): return "func() called"
  9. ...
  10. >>>
  11. >>>
  12. >>> f = open("other_pickles", "wb")
  13. >>>
  14. >>> pickle.dump(My_class, f) # serialize class object
  15. >>>
  16. >>> pickle.dump(2 + 3j, f) # serialize complex number
  17. >>>
  18. >>> pickle.dump(func, f) # serialize function object
  19. >>>
  20. >>> pickle.dump(bytes([1, 2, 3, 4, 5]), f) # serialize bytes object
  21. >>>
  22. >>> pickle.dump(My_class("name"), f) # serialize class instance
  23. >>>
  24. >>> f.close()
  25. >>>
  26. >>>

我们现在转储了一些数据。 此时,如果您尝试从文件中读取数据,则会将数据作为bytes对象获得。

  1. >>>
  2. >>> open("my_pickle", "rb").read()
  3. 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.'
  4. >>>
  5. >>>
  6. >>> open("other_pickles", "rb").read()
  7. 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.'
  8. >>>
  9. >>>

这不是很可读。 对?

要恢复拾取的对象,我们使用load()函数

load()加载


load()函数获取一个文件对象,从转储的表示中重建对象,然后将其返回。

其语法如下:

参数 描述
file 从中读取序列化数据的文件对象。

现在,让我们尝试阅读我们在本文前面创建的my_pickle文件。

  1. >>>
  2. >>> f = open("my_pickle", "rb")
  3. >>>
  4. >>> pickle.load(f)
  5. 10
  6. >>> pickle.load(f)
  7. 'a string'
  8. >>>
  9. >>> pickle.load(f)
  10. {'b': 2, 'a': 1}
  11. >>>
  12. >>> pickle.load(f)
  13. datetime.datetime(2018, 9, 30, 16, 46, 30, 866706)
  14. >>>
  15. >>> pickle.load(f)
  16. Traceback (most recent call last):
  17. File "<stdin>", line 1, in <module>
  18. EOFError: Ran out of input
  19. >>>
  20. >>> f.close()
  21. >>>

注意,对象的返回顺序与我们首先对其进行转储的顺序相同。 另外,请注意,该文件以二进制模式打开以进行读取。 当没有更多数据要返回时,load()函数将引发EOFError

同样,我们可以从other_pickles文件中读取转储的数据。

  1. >>>
  2. >>>
  3. >>> f = open("other_pickles", "rb") # open the file for reading in binary mode
  4. >>>
  5. >>> My_class = pickle.load(f)
  6. <class '__main__.My_class'>
  7. >>>
  8. >>>
  9. >>> c = pickle.load(f)
  10. >>>
  11. >>> c
  12. (2+3j)
  13. >>>
  14. >>>
  15. >>> func = pickle.load(f)
  16. >>>
  17. >>> func
  18. <function func at 0x7f9aa6ab6488>
  19. >>>
  20. >>>
  21. >>> b = pickle.load(f)
  22. >>>
  23. >>> b
  24. b'\x01\x02\x03\x04\x05'
  25. >>>
  26. >>>
  27. >>> my_class_obj = pickle.load(f)
  28. >>> my_class_obj
  29. <__main__.My_Class object at 0x7f9aa74e61d0>
  30. >>>
  31. >>>
  32. >>> pickle.load(f)
  33. Traceback (most recent call last):
  34. File "<stdin>", line 1, in <module>
  35. EOFError: Ran out of input
  36. >>>
  37. >>>
  38. >>> f.close()
  39. >>>
  40. >>>

加载数据后,就可以像普通的 Python 对象一样使用它了。

  1. >>>
  2. >>> func()
  3. 'func() called'
  4. >>>
  5. >>>
  6. >>> c.imag, c.real
  7. (3.0, 2.0)
  8. >>>
  9. >>>
  10. >>> My_class("Tom")
  11. <__main__.My_Class object at 0x7f9aa74e6358>
  12. >>>
  13. >>>
  14. >>> my_class_obj.name
  15. 'name'
  16. >>>
  17. >>>

使用dumps()load()进行转储和加载


dumps()的工作方式与dump()完全相同,但是它不是将输出发送到文件,而是将转储的数据作为字符串返回。 其语法如下:

语法dumps(obj) -> pickled_data

参数 描述
obj 要序列化的对象

同样,loads()函数与load()相同,但是它不是从文件中读取转储的数据,而是从字符串中读取数据。 其语法如下:

语法loads(pickled_data) -> obj

参数 描述
pickled_data 转储数据

Here is an example:

  1. >>>
  2. >>> employee = {
  3. ... "first_name": "Mike",
  4. ... "designation": 'Manager',
  5. ... "doj": datetime(year=2016, month=5, day=2), # date of joining
  6. ... }
  7. >>>
  8. >>>
  9. >>> pickled_emp = pickle.dumps(employee) # pickle employee dictionary
  10. >>>
  11. >>> pickled_emp
  12. 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.'
  13. >>>
  14. >>>
  15. >>> pickle.loads(pickled_emp) # unpickle employee dictionary
  16. {'designation': 'Manager', 'doj': datetime.datetime(2016, 5, 2, 0, 0), 'first_name': 'Mike'}
  17. >>>
  18. >>>

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