为什么pickle协议2让我序列化一个打开的文件对象?

时间:2011-08-18 12:28:40

标签: python pickle python-2.x

考虑:

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

似乎完全合理 - 当然我不能腌制一个打开的文件句柄。但是:

>>> pickle.dumps(thing, 2)
'\x80\x02c__builtin__\nfile\nq\x00)\x81q\x01.'
>>> pickle.loads(pickle.dumps(thing, 2))
<closed file '<uninitialized file>', mode '<uninitialized file>' at 0x7ff3c078>

显然我可以腌制一个打开的文件,只是没有用。

这是故意的吗?它掩盖了我的代码中的一个错误,我错误地腌制了一个拥有文件的对象。在某些情况下,该对象还包含pyodbc光标,结果相同。

我在PEP 307中没有看到任何关于它的内容。这只是一个疏忽,还是有一些重要的事情发生在我想念中,即使用协议2进行酸洗,也能让我得到我想要的异常?

我正在使用Python 2.6.5。我知道,我知道,但这就是我的发行版。

2 个答案:

答案 0 :(得分:10)

Python Wiki上,它说

  

您无法挑选打开的文件对象,网络连接或数据库连接。当你考虑它时,它是有道理的 - 当你取消对象时,pickle不能存在文件对象的连接,并且创建该连接的过程超出了pickle可以自动为你做的事情。如果您真的想要挑选具有导致问题的属性的内容,请查看__getstate____setstate____getinitargs__的pickle文档 - 使用这些文档可以排除有问题的属性。

但是,我发现this bug report表示您实际上可以挑选文件对象。这似乎是无意的。它已在Python 3.2中修复。

如果您想要阻止它发生,您可以看看是否可以将该补丁修改为Python 2.6。否则,你只需要小心你所腌制的东西。

答案 1 :(得分:0)

如果您正在寻找更好的行为,可以使用dill ...它不会序列化类文件对象,但知道如何序列化文件句柄。行为是,如果文件存在,dill将指向反序列化的文件句柄...如果文件不存在,则文件句柄将被关闭。

Python 2.7.8 (default, Jul  3 2014, 05:59:29) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>>        
>>> thing = open('foobar.txt', 'w')
>>> thing
<open file 'foobar.txt', mode 'w' at 0x10e3c2c00>
>>> dill.loads(dill.dumps(thing))
<open file 'foobar.txt', mode 'w' at 0x10e3c2c90>
>>> 

在此处获取dillhttps://github.com/uqfoundation