web-dev-qa-db-ja.com

オブジェクトがピクルされない理由の確立

タイプtのAPIからオブジェクトObjectを受信して​​います。私はそれを漬けることができず、エラーが出ます:

  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ポインターが含まれている可能性もあります。その場合、オブジェクトをピクルすることは意味がありません。しかし、これを確立するために、オブジェクトの内部構造をもう一度見たいと思います。

9
Baz

python docs を読んで、APIのObjectクラスを後で確認することもできます。

「オブジェクトの内部構造」に関しては、通常、インスタンス属性は__dict__属性に格納されます(そして、クラス属性はピクルされないため、インスタンス属性のみに注意する必要があります)-ただし、各属性の__dict__sを再帰的に検査します。

3

私はdillを使用します。これには、オブジェクトの内部でターゲットオブジェクトがpickle化されない原因を調査するツールがあります。例についてはこの回答を参照してください: DillモジュールのBadItemの良い例 、および実際に使用されている検出ツールの例については、このQ&A: pandas.algos._return_falseはディルでPicklingErrorを引き起こします。 CentOSのdump_session

>>> 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にないこれらのコードの機能をすぐにマージしたいと思っています。

8
Mike McKerns

ディルを試しましたが、問題は説明されませんでした。代わりに、 https://Gist.github.com/andresriancho/15b5e226de68a0c2efd から次のコードを使用しました。これにより、__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__によってマングルされているためです。

1

これが Alastairの解 の拡張ですPython 3。

それ:

  • 問題は何層にも及ぶ可能性のある複雑なオブジェクトを処理するために再帰的です。

    出力は.x[i].y.z....の形式であり、問​​題を解決するために呼び出されたメンバーを確認できます。 dictを使用すると、代わりに[key/val type=...]が出力されます。キーまたは値のいずれかが問題となり、dict内の特定のキーまたは値を参照することが困難になります(不可能ではありません)。 。

  • より多くの型、特にlistTupleおよびdictのアカウント。これらは__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
0
Dukeling