繁体   English   中英

确定无法腌制对象的原因

[英]Establishing why an object can't be pickled

我从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指针,在这种情况下,我对pickle对象没有意义。 但我想再次看到对象的内部结构,以便建立这个。

我会使用dill ,它有工具来调查对象内部导致目标对象无法被选择的工具。 请参阅此答案以获取示例: Dill模块中BadItem的良好示例 ,以及此实际使用中检测工具示例的问答: pandas.algos._return_false导致在CentOS上使用dill.dump_session进行PicklingError

>>> 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缺少的任何功能。

您可能希望阅读python文档并在之后检查API的Object类。

关于“对象的内部结构”,通常实例属性存储在__dict__属性中(并且由于类属性不是pickle,你只关心实例属性) - 但请注意,你还必须递归检查__dict__为每个属性。

我试过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__正在破坏属性名称

这是Alastair解决方案的扩展,在Python 3中。

它:

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

    输出的格式为.x[i].yz... ,以便您查看调用哪些成员来解决问题。 使用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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM