代码之家  ›  专栏  ›  技术社区  ›  nikow

如何判断哪个对象属性pickle失败?

  •  30
  • nikow  · 技术社区  · 16 年前

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

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

    有没有办法知道是哪个属性导致了异常?我使用的是python 2.5.2。

    尽管我原则上理解了问题的根本原因(例如,在上面的例子中有一个实例方法),但是仍然很难 精确定位 . 在我的例子中,我已经定义了一个自定义 __getstate__ 方法,但忘记了关键属性。这发生在一个复杂的嵌套对象结构中,因此我花了一段时间来识别坏的属性。

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

    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 导致问题。

    4 回复  |  直到 16 年前
        1
  •  15
  •   joeforker    16 年前

    您可以针对python提交一个bug,因为它没有包含更多有用的错误消息。同时,修改 _reduce_ex() 功能在 copy_reg.py .

    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
    
        2
  •  8
  •   koffie    14 年前

    我和你有同样的问题,但我的课程有点复杂(例如,一棵类似对象的大树),所以打印没有太多帮助,所以我拼凑了一个助手函数。不完整,仅用于酸洗方案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是不pickle的对象

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

    这就意味着“K”这个角色是不可选择的,而“K”这个角色也同样适用于“K”这个角色。

        3
  •  3
  •   Stuart Mitchell    13 年前

    我发现,如果您将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)
    
        4
  •  3
  •   Mike McKerns    11 年前

    如果你使用 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.'
    

    所以我们必须找到 小茴香 不能泡菜…

    >>> 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'>])
    

    小茴香 不依赖于 __getstate__ 方法是存在的,尽管如果它存在的话也许应该利用它。您也可以使用 objgraph 获取用于构建不pickle的对象的所有对象依赖关系的图片。根据以上信息,它可以帮助您找出问题的根源。

    侦测 用于跟踪此问题中不可选取的项目: https://github.com/uqfoundation/dill/issues/58

    推荐文章