如何在大型对象的Python Pickle中查找错误来源

7 投票
4 回答
4493 浏览
提问于 2025-04-16 20:54

我接手了一个人的代码,项目比较大。我想保存程序的状态,有一个很大的对象几乎存储了所有其他对象。我试着把这个对象进行序列化(也就是“腌制”),但是遇到了一个错误:

pickle.PicklingError: 无法腌制:找不到 builtin.module

根据我在谷歌上找到的信息,这个错误可能是因为我在某个地方导入了Python初始化之外的东西,或者是某个类的属性引用了一个模块。所以,我有两个问题:

  1. 有没有人能确认这是导致这个错误的原因吗?我在代码中查找的方向对吗?

  2. 有没有办法找到是哪个代码行或对象成员导致了腌制的问题?错误追踪信息只显示了腌制时出错的那一行,而不是被腌制对象的那一行。

4 个回答

2

如果你想快速找到是什么对象的属性或成员导致了问题,可以试试下面的方法:

for k, v in massiveobject.__dict__.iteritems():
    print k
    pickle.dumps(v)
5

dill 里,有类似的东西。我们来看一个对象的列表,看看我们能做些什么:

>>> import dill
>>> f = open('whatever', 'w')
>>> f.close()
>>> 
>>> l = [iter([1,2,3]), xrange(5), open('whatever', 'r'), lambda x:x]
>>> dill.detect.trace(False)
>>> dill.pickles(l)
False

好吧,dill 无法对这个列表进行序列化。那么问题出在哪里呢?

>>> dill.detect.trace(True)
>>> dill.pickles(l)
T4: <type 'listiterator'>
False

好的,列表中的第一个项目无法序列化。那其他的呢?

>>> map(dill.pickles, l)
T4: <type 'listiterator'>
Si: xrange(5)
F2: <function _eval_repr at 0x106991cf8>
Fi: <open file 'whatever', mode 'r' at 0x10699c810>
F2: <function _create_filehandle at 0x106991848>
B2: <built-in function open>
F1: <function <lambda> at 0x1069f6848>
F2: <function _create_function at 0x1069916e0>
Co: <code object <lambda> at 0x105a0acb0, file "<stdin>", line 1>
F2: <function _unmarshal at 0x106991578>
D1: <dict object at 0x10591d168>
D2: <dict object at 0x1069b1050>
[False, True, True, True]

嗯,其他的对象都能顺利序列化。那么,我们来替换掉第一个对象。

>>> dill.detect.trace(False)
>>> l[0] = xrange(1,4)
>>> dill.pickles(l)
True
>>> _l = dill.loads(dill.dumps(l))

现在我们的对象可以序列化了。其实,我们可能在利用 Linux/Unix/Mac 上进行序列化时的一些内置对象共享功能……那么我们来做个更严格的检查,比如在子进程中进行序列化(就像在 Windows 上那样)?

>>> dill.check(l)        
[xrange(1, 4), xrange(5), <open file 'whatever', mode 'r' at 0x107998810>, <function <lambda> at 0x1079ec410>]
>>> 

不,这个列表依然可以正常工作……所以这是一个可以成功发送到另一个进程的对象。

现在,关于你的错误,大家似乎都忽略了……

ModuleType 对象是无法序列化的,这就是导致你错误的原因。

>>> import types
>>> types.ModuleType 
<type 'module'>
>>>
>>> import pickle
>>> pickle.dumps(types.ModuleType)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1374, in dumps
    Pickler(file, protocol).dump(obj)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 224, in dump
    self.save(obj)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 748, in save_global
    (obj, module, name))
pickle.PicklingError: Can't pickle <type 'module'>: it's not found as __builtin__.module

不过,如果我们导入 dill,它就神奇地能工作了。

>>> import dill
>>> pickle.dumps(types.ModuleType)
"cdill.dill\n_load_type\np0\n(S'ModuleType'\np1\ntp2\nRp3\n."
>>> 
13

2) 你可以通过创建一个新的类来继承pickle.Pickler,并对它进行一些修改,这样就能记录下它正在处理哪些数据。这会让你更容易找到问题出在哪里。

import pickle
class MyPickler (pickle.Pickler):
    def save(self, obj):
        print 'pickling object', obj, 'of type', type(obj)
        pickle.Pickler.save(self, obj)

这个方法只适用于Python版本的pickle.Pickler。在Python 3.x中,pickle模块默认使用C语言实现的版本,而纯Python的版本叫做_Pickler。

# Python 3.x
import pickle
class MyPickler (pickle._Pickler):
    def save(self, obj):
        print ('pickling object  {0} of type {1}'.format(obj, type(obj))
        pickle._Pickler.save(self, obj)

撰写回答