ピクルスできない属性を持つオブジェクトをピクルス化すると、次のような一般的なエラーメッセージが表示されて失敗します。
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
が問題を引き起こすというヒントはありません。
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
_
私はあなたと同じ問題を抱えていましたが、私のクラスはもう少し複雑でした(つまり、同様のオブジェクトの大きなツリー)ので、印刷はあまり役に立たなかったので、ヘルパー関数を一緒にハックしました。これは完全ではなく、酸洗いプロトコル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にも当てはまります。
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
Picklerをサブクラス化し、Pickler.save()メソッドをtryでラップすると、ブロックを除くことがわかりました。
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)