【问题标题】:How to tell for which object attribute pickle fails?如何判断哪个对象属性泡菜失败?
【发布时间】:2009-02-20 14:36:09
【问题描述】:

当您腌制具有某些无法腌制的属性的对象时,它将失败并显示如下一般错误消息:

PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup __builtin__.instancemethod failed

有什么方法可以判断是哪个属性导致了异常?我正在使用 Python 2.5.2。

即使我原则上了解问题的根本原因(例如,在上面的示例中具有实例方法),仍然很难准确定位。在我的例子中,我已经定义了一个自定义的 __getstate__ 方法,但是忘记了一个关键属性。这发生在嵌套对象的复杂结构中,所以我花了一段时间才确定坏属性。

根据要求,这里有一个简单的例子,泡菜故意失败:

import cPickle as pickle
import new

class Test(object):
    pass

def test_func(self):
    pass

test = Test()
pickle.dumps(test)
print "now with instancemethod..."
test.test_meth = new.instancemethod(test_func, test)
pickle.dumps(test)

这是输出:

now with instancemethod...
Traceback (most recent call last):
  File "/home/wilbert/develop/workspace/Playground/src/misc/picklefail.py", line 15, in <module>
    pickle.dumps(test)
  File "/home/wilbert/lib/python2.5/copy_reg.py", line 69, in _reduce_ex
    raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle instancemethod objects

不幸的是,没有提示属性test_meth 会导致问题。

【问题讨论】:

  • 你能举一个失败属性的小例子吗?或者至少显示更多的回溯,以查看在 pickle 模块中失败的位置?
  • 哦,您使用的是哪个 Python 版本?
  • @MrTopf:添加信息
  • 谢谢,但实际上我认为您唯一的选择确实是打补丁(并提交错误报告)。

标签: python serialization


【解决方案1】:

您可以针对 Python 提交错误,因为它不包含更多有用的错误消息。同时修改copy_reg.py中的_reduce_ex()函数。

if base is self.__class__:
    print self # new   
    raise TypeError, "can't pickle %s objects" % base.__name__

输出:

<bound method ?.test_func of <__main__.Test object at 0xb7f4230c>>
Traceback (most recent call last):
  File "nopickle.py", line 14, in ?
    pickle.dumps(test)
  File "/usr/lib/python2.4/copy_reg.py", line 69, in _reduce_ex
    raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle instancemethod objects

【讨论】:

  • 为什么不简单地将“self”放入错误消息而不是打印?
  • 我相信这涵盖了示例中的“TypeError”异常,但是没有解决原始的“PicklingError”异常。
  • 我认为这应该是 Python 的一部分,或者至少与 raise 语句合并。
  • 很难相信公认的答案是修改 python 发行版并重新编译。 (1)不修改python也可以做同样的事情,(2)如果要做这样的事情,至少要给python提交一个补丁。
  • @dashesy:我的和其他两个答案都是找出哪些属性导致酸洗错误的可行方法。 dill.detect 可用于检查哪些对象无法使用dill 腌制。如果您感兴趣的库使用pickle,并且您想使用dill……只需键入import dill,它就会将dill 注册类型加载到pickle 注册表中……无需编辑第3 方代码。如果你想使用dill 来检测错误,而不是增加python 的pickle 模块……然后输入:dill.extend(False)
【解决方案2】:

我和你有同样的问题,但是我的类有点复杂(即类似对象的大树)所以打印没有太大帮助,所以我一起破解了一个辅助函数。它不完整,仅适用于酸洗协议 2: 这足以让我找到我的问题。如果您想扩展它以涵盖所有内容,该协议在http://www.python.org/dev/peps/pep-0307/ 中进行了描述,我已将这篇文章设为可编辑,因此每个人都可以更新代码。

import pickle
def get_pickling_errors(obj,seen=None):
    if seen == None:
        seen = []
    try:
        state = obj.__getstate__()
    except AttributeError:
        return
    if state == None:
        return
    if isinstance(state,tuple):
        if not isinstance(state[0],dict):
            state=state[1]
        else:
            state=state[0].update(state[1])
    result = {}    
    for i in state:
        try:
            pickle.dumps(state[i],protocol=2)
        except pickle.PicklingError:
            if not state[i] in seen:
                seen.append(state[i])
                result[i]=get_pickling_errors(state[i],seen)
    return result

K 是不腌制的对象的用法示例

>>> get_pickling_errors(K)
{'_gen': {}, '_base': {'_gens': None}}

这意味着属性 K._gen 不可提取,对于 K._base._gens 也是如此。

【讨论】:

  • 谢谢,它有帮助。我认为__getstate__()应该在相应的类中定义。
【解决方案3】:

我发现如果你继承 Pickler 并在 try 中包装 Pickler.save() 方法,除了块

import pickle
class MyPickler (pickle.Pickler):
    def save(self, obj):
        try:
            pickle.Pickler.save(self, obj)
        except Exception, e:
            import pdb;pdb.set_trace()

那就这样称呼吧

import StringIO
output = StringIO.StringIO()
MyPickler(output).dump(thingee)

【讨论】:

  • 这不就是启动调试器吗?与例如仅使用 Eclipse PyDev Debugger 来查看问题有什么区别吗?
  • 如果 PyDev 调试器将启动异常并将您置于正确的执行级别,它是相同的。不过我不使用 IDE。
  • 我用它来定位一个特别难发现的 Pickle 错误。但我没有让 except 子句启动调试器,而是让它打印 obj 并引发异常。然后,运行 MyPickler(output).dump(obj) 会生成一个很好的类似回溯的报告,该报告准确地说明了不可腌制对象在我的深层嵌套结构中的确切位置。多么美好的一天。
【解决方案4】:

如果您使用dill,您的示例不会失败...

>>> import dill
>>> import new
>>> 
>>> class Test(object):
...     pass
... 
>>> def test_func(self):
...     pass
... 
>>> test = Test()
>>> dill.dumps(test)
'\x80\x02cdill.dill\n_create_type\nq\x00(cdill.dill\n_load_type\nq\x01U\x08TypeTypeq\x02\x85q\x03Rq\x04U\x04Testq\x05h\x01U\nObjectTypeq\x06\x85q\x07Rq\x08\x85q\t}q\n(U\r__slotnames__q\x0b]q\x0cU\n__module__q\rU\x08__main__q\x0eU\x07__doc__q\x0fNutq\x10Rq\x11)\x81q\x12}q\x13b.'
>>> test.test_meth = new.instancemethod(test_func, test)
>>> dill.dumps(test)
'\x80\x02cdill.dill\n_create_type\nq\x00(cdill.dill\n_load_type\nq\x01U\x08TypeTypeq\x02\x85q\x03Rq\x04U\x04Testq\x05h\x01U\nObjectTypeq\x06\x85q\x07Rq\x08\x85q\t}q\n(U\r__slotnames__q\x0b]q\x0cU\n__module__q\rU\x08__main__q\x0eU\x07__doc__q\x0fNutq\x10Rq\x11)\x81q\x12}q\x13U\ttest_methq\x14h\x01U\nMethodTypeq\x15\x85q\x16Rq\x17cdill.dill\n_create_function\nq\x18(cdill.dill\n_unmarshal\nq\x19Ubc\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00C\x00\x00\x00s\x04\x00\x00\x00d\x00\x00S(\x01\x00\x00\x00N(\x00\x00\x00\x00(\x01\x00\x00\x00t\x04\x00\x00\x00self(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>t\t\x00\x00\x00test_func\x01\x00\x00\x00s\x02\x00\x00\x00\x00\x01q\x1a\x85q\x1bRq\x1cc__builtin__\n__main__\nU\ttest_funcq\x1dNN}q\x1etq\x1fRq h\x12N\x87q!Rq"sb.'

所以我们必须找到dill不能腌制的东西......

>>> class Unpicklable(object):
...   def breakme(self):
...     self.x = iter(set())
... 
>>> u = Unpicklable()
>>> dill.dumps(u)
'\x80\x02cdill.dill\n_create_type\nq\x00(cdill.dill\n_load_type\nq\x01U\x08TypeTypeq\x02\x85q\x03Rq\x04U\x0bUnpicklableq\x05h\x01U\nObjectTypeq\x06\x85q\x07Rq\x08\x85q\t}q\n(U\r__slotnames__q\x0b]q\x0cU\n__module__q\rU\x08__main__q\x0eU\x07breakmeq\x0fcdill.dill\n_create_function\nq\x10(cdill.dill\n_unmarshal\nq\x11U\xafc\x01\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00C\x00\x00\x00s"\x00\x00\x00d\x01\x00d\x00\x00l\x00\x00}\x01\x00t\x01\x00t\x02\x00\x83\x00\x00\x83\x01\x00|\x00\x00_\x03\x00d\x00\x00S(\x02\x00\x00\x00Ni\xff\xff\xff\xff(\x04\x00\x00\x00t\t\x00\x00\x00itertoolst\x04\x00\x00\x00itert\x03\x00\x00\x00sett\x01\x00\x00\x00x(\x02\x00\x00\x00t\x04\x00\x00\x00selfR\x00\x00\x00\x00(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>t\x07\x00\x00\x00breakme\x02\x00\x00\x00s\x04\x00\x00\x00\x00\x01\x0c\x01q\x12\x85q\x13Rq\x14c__builtin__\n__main__\nh\x0fNN}q\x15tq\x16Rq\x17U\x07__doc__q\x18Nutq\x19Rq\x1a)\x81q\x1b}q\x1cb.'
>>> u.breakme()
>>> dill.dumps(u)
Traceback (most recent call last):
…(snip)… 
pickle.PicklingError: Can't pickle <type 'setiterator'>: it's not found as __builtin__.setiterator
>>>

如果错误信息不好,我可以使用dill.detect 来查看顶级对象包含哪些不可拾取的对象。

>>> dill.detect.badobjects(u, depth=1)
{'__hash__': <method-wrapper '__hash__' of Unpicklable object at 0x10a37b350>, '__setattr__': <method-wrapper '__setattr__' of Unpicklable object at 0x10a37b350>, '__reduce_ex__': <built-in method __reduce_ex__ of Unpicklable object at 0x10a37b350>, '__reduce__': <built-in method __reduce__ of Unpicklable object at 0x10a37b350>, '__str__': <method-wrapper '__str__' of Unpicklable object at 0x10a37b350>, '__format__': <built-in method __format__ of Unpicklable object at 0x10a37b350>, '__getattribute__': <method-wrapper '__getattribute__' of Unpicklable object at 0x10a37b350>, '__delattr__': <method-wrapper '__delattr__' of Unpicklable object at 0x10a37b350>, 'breakme': <bound method Unpicklable.breakme of <__main__.Unpicklable object at 0x10a37b350>>, '__repr__': <method-wrapper '__repr__' of Unpicklable object at 0x10a37b350>, '__dict__': {'x': <setiterator object at 0x10a370820>}, 'x': <setiterator object at 0x10a370820>, '__sizeof__': <built-in method __sizeof__ of Unpicklable object at 0x10a37b350>, '__init__': <method-wrapper '__init__' of Unpicklable object at 0x10a37b350>}
>>> dill.detect.badtypes(u, depth=1)
{'__hash__': <type 'method-wrapper'>, '__setattr__': <type 'method-wrapper'>, '__reduce_ex__': <type 'builtin_function_or_method'>, '__reduce__': <type 'builtin_function_or_method'>, '__str__': <type 'method-wrapper'>, '__format__': <type 'builtin_function_or_method'>, '__getattribute__': <type 'method-wrapper'>, '__delattr__': <type 'method-wrapper'>, 'breakme': <type 'instancemethod'>, '__repr__': <type 'method-wrapper'>, '__dict__': <type 'dict'>, 'x': <type 'setiterator'>, '__sizeof__': <type 'builtin_function_or_method'>, '__init__': <type 'method-wrapper'>}
>>> set(dill.detect.badtypes(u, depth=1).values())
set([<type 'dict'>, <type 'method-wrapper'>, <type 'instancemethod'>, <type 'setiterator'>, <type 'builtin_function_or_method'>])

dill 不依赖于存在的__getstate__ 方法,尽管如果存在它也许应该使用它。您还可以使用objgraph 获取用于构建不腌制事物的所有对象依赖项的图片。根据以上信息,它可以帮助您理清问题的根源。

请参阅dill.detect 在此问题中用于追踪不可拾取的项目:https://github.com/uqfoundation/dill/issues/58

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-03
    • 1970-01-01
    • 1970-01-01
    • 2015-07-23
    • 2012-06-09
    • 1970-01-01
    相关资源
    最近更新 更多