I'm receiving an object, t
, from an api of type Object
. I am unable to pickle it, getting the error:
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>
When I do the following:
for i in dir(t): print(type(i))
I get only string objects:
<type 'str'>
<type 'str'>
<type 'str'>
...
<type 'str'>
<type 'str'>
<type 'str'>
How can I print the contents of my Object
object in order to understand why it cant be pickled?
Its also possible that the object contains C pointers to QT objects, in which case it wouldn't make sense for me to pickle the object. But again I would like to see the internal structure of the object in order to establish this.
I would use dill
, which has tools to investigate what inside an object causes your target object to not be picklable. See this answer for an example: Good example of BadItem in Dill Module, and this Q&A for an example of the detection tools in real use: 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)
[]
However, the most common starting point is to use 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
also has functionality to trace pointers referrers and referents to objects, so you can build a hierarchy of how objects refer to each other. See: https://github.com/uqfoundation/dill/issues/58
Alternately, there's also: cloudpickle.py and debugpickle.py, which are for the most part no longer developed. I'm the dill
author, and hope to soon merge any functionality in these codes that is missing in dill
.
Here's an extension of Alastair's solution, in Python 3.
It:
is recursive, to deal with complex objects where the problem might be many layers deep.
The output is in the form .x[i].y.z....
to allow you to see which members were called to get to the problem. With dict
it just prints [key/val type=...]
instead, since either keys or values can be the problem, making it harder (but not impossible) to reference a specific key or value in the dict
.
accounts for more types, specifically list
, tuple
and dict
, which need to be handled separately, since they don't have __dict__
attributes.
returns all problems, rather than just the first one.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With