【问题标题】:Establishing why an object can't be pickled确定不能腌制对象的原因
【发布时间】:2015-05-28 07:00:26
【问题描述】:

我从 Object 类型的 api 接收对象 t。我无法腌制它,收到错误:

  File "p.py", line 55, in <module>
    pickle.dump(t, open('data.pkl', 'wb'))
  File "/usr/lib/python2.6/pickle.py", line 1362, in dump
    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 313, in save
    (t.__name__, obj))
pickle.PicklingError: Can't pickle 'Object' object: <Object object at 0xb77b11a0>

当我执行以下操作时:

for i in dir(t): print(type(i))

我只得到字符串对象:

<type 'str'>
<type 'str'>
<type 'str'>
...
<type 'str'>
<type 'str'>
<type 'str'>

如何打印我的 Object 对象的内容以了解为什么它不能被腌制?

对象也有可能包含指向 QT 对象的 C 指针,在这种情况下,我对对象进行腌制是没有意义的。但是我想再次查看对象的内部结构以建立这一点。

【问题讨论】:

  • 这似乎是您可以通过调查 Object 类型本身而不是实例来调试的东西。
  • dir() 返回字符串变量名列表,而不是变量本身的值。

标签: python pickle


【解决方案1】:

我会使用dill,它有工具可以调查对象内部是什么导致您的目标对象不可腌制。示例请参见此答案:Good example of BadItem in Dill Module,实际使用中的检测工具示例请参见此问答:pandas.algos._return_false causes PicklingError with dill.dump_session on CentOS

>>> import dill
>>> x = iter([1,2,3,4])
>>> d = {'x':x}
>>> # we check for unpicklable items in d (i.e. the iterator x)
>>> dill.detect.baditems(d)
[<listiterator object at 0x10b0e48d0>]
>>> # note that nothing inside of the iterator is unpicklable!
>>> dill.detect.baditems(x)
[]

不过,最常见的起点是使用trace

>>> dill.detect.trace(True)
>>> dill.detect.errors(d)
D2: <dict object at 0x10b8394b0>
T4: <type 'listiterator'>
PicklingError("Can't pickle <type 'listiterator'>: it's not found as __builtin__.listiterator",)
>>> 

dill 还具有跟踪指针引用者和对象所指对象的功能,因此您可以构建对象如何相互引用的层次结构。见:https://github.com/uqfoundation/dill/issues/58

另外,还有:cloudpickle.py 和 debugpickle.py,它们大部分已不再开发。我是dill 的作者,希望尽快合并这些代码中dill 中缺少的任何功能。

【讨论】:

  • 有趣的是我什至用莳萝有这个:-(
  • @iirekm:你能澄清一下“这个”是什么意思吗?
【解决方案2】:

您可能需要阅读 python docs 并在之后检查您的 API 的 Object 类。

关于“对象的内部结构”,通常实例属性存储在__dict__ 属性中(并且由于类属性没有被腌制,您只关心实例属性) - 但请注意,您还将必须递归检查每个属性的__dict__s。

【讨论】:

    【解决方案3】:

    我尝试了 Dill,但它没有解释我的问题。相反,我使用了来自https://gist.github.com/andresriancho/15b5e226de68a0c2efd0 的以下代码,这恰好在我的__getattribute__ 覆盖中显示了一个错误:

    def debug_pickle(instance):
      """
      :return: Which attribute from this object can't be pickled?
      """
      attribute = None
    
      for k, v in instance.__dict__.iteritems():
          try:
              cPickle.dumps(v)
          except:
              attribute = k
              break
    
      return attribute
    

    编辑:这是我的代码的复制品,使用 pickle 和 cPickle:

    class myDict(dict):
    
        def __getattribute__(self, item):
            # Try to get attribute from internal dict
            item = item.replace("_", "$")
    
            if item in self:
                return self[item]
    
            # Try super, which may leads to an AttribueError
            return super(myDict, self).__getattribute__(item)
    
    myd = myDict()
    
    try: 
        with open('test.pickle', 'wb') as myf:
            cPickle.dump(myd, myf, protocol=-1)
    except:
        print traceback.format_exc()
    
    
    try:
        with open('test.pickle', 'wb') as myf:
            pickle.dump(myd, myf, protocol=-1)
    except:
        print traceback.format_exc()
    

    输出:

    Traceback (most recent call last):
    File "/Users/myuser/Documents/workspace/AcceptanceTesting/ingest.py", line 35, in <module>
      cPickle.dump(myd, myf, protocol=-1)
    UnpickleableError: Cannot pickle <class '__main__.myDict'> objects
    
    Traceback (most recent call last):
    File "/Users/myuser/Documents/workspace/AcceptanceTesting/ingest.py", line 42, in <module>
      pickle.dump(myd, myf, protocol=-1)
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1370, in dump
      Pickler(file, protocol).dump(obj)
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 224, in dump
      self.save(obj)
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 313, in save
      (t.__name__, obj))
    PicklingError: Can't pickle 'myDict' object: {}
    

    您会看到原因是因为属性名称被__getattribute__ 破坏了

    【讨论】:

    • 你能澄清你的意思dill没有帮助,但上面的代码有帮助吗?上面的代码是存在于dill.detect 中的较弱版本的代码。具体来说,dill.detect.badobjectsdill.detect.badtypesdill 还具有我的答案中显示的其他工具。如果这里缺少dill 的内容,我想知道。
    • 嗨@MikeMcKerns。莳萝看起来很棒,我只是认为你不能为我的愚蠢编码:) 我已经用我的错误代码的复制更新了我的问题。
    • 啊……好吧,你有一个真正的角落案例。 dill 没有发现本案的问题,我同意。但是,您还会注意到 debug_pickle 实际上并没有返回 bad 属性……它只是碰巧没有捕获导致酸洗失败的错误。有用,但似乎是无意的。我会记下这个案例作为dill 的票。谢谢。
    【解决方案4】:

    这是Alastair's solution 在 Python 3 中的扩展。

    它:

    • 是递归的,用于处理问题可能很深的复杂对象。

      输出格式为.x[i].y.z....,以便您查看调用了哪些成员来解决问题。使用dict,它只会打印[key/val type=...],因为键或值都可能是问题所在,这使得在dict 中引用特定键或值变得更加困难(但并非不可能)。

    • 占更多类型,具体是listtupledict,由于没有__dict__属性,需要单独处理。

    • 返回所有问题,而不仅仅是第一个问题。

    def get_unpicklable(instance, exception=None, string='', first_only=True):
        """
        Recursively go through all attributes of instance and return a list of whatever
        can't be pickled.
    
        Set first_only to only print the first problematic element in a list, tuple or
        dict (otherwise there could be lots of duplication).
        """
        problems = []
        if isinstance(instance, tuple) or isinstance(instance, list):
            for k, v in enumerate(instance):
                try:
                    pickle.dumps(v)
                except BaseException as e:
                    problems.extend(get_unpicklable(v, e, string + f'[{k}]'))
                    if first_only:
                        break
        elif isinstance(instance, dict):
            for k in instance:
                try:
                    pickle.dumps(k)
                except BaseException as e:
                    problems.extend(get_unpicklable(
                        k, e, string + f'[key type={type(k).__name__}]'
                    ))
                    if first_only:
                        break
            for v in instance.values():
                try:
                    pickle.dumps(v)
                except BaseException as e:
                    problems.extend(get_unpicklable(
                        v, e, string + f'[val type={type(v).__name__}]'
                    ))
                    if first_only:
                        break
        else:
            for k, v in instance.__dict__.items():
                try:
                    pickle.dumps(v)
                except BaseException as e:
                    problems.extend(get_unpicklable(v, e, string + '.' + k))
    
        # if we get here, it means pickling instance caused an exception (string is not
        # empty), yet no member was a problem (problems is empty), thus instance itself
        # is the problem.
        if string != '' and not problems:
            problems.append(
                string + f" (Type '{type(instance).__name__}' caused: {exception})"
            )
    
        return problems
    

    【讨论】:

      猜你喜欢
      • 2016-03-27
      • 2018-10-30
      • 2022-07-16
      • 2014-08-02
      • 2021-08-18
      • 2020-06-26
      • 1970-01-01
      • 1970-01-01
      • 2019-12-14
      相关资源
      最近更新 更多