How can I modify the classes below to make them pickeable?
This question: How to make a class which has __getattr__ properly pickable? is similar but refer to wrong exception in the use of getattr.
This other question seems to provide meaningful insight Why does pickle.dumps call __getattr__?, however it fails to provide an example, and I honestly cannot understand what I am suppose to implement.
import pickle
class Foo(object):
def __init__(self, dct):
for key in dct:
setattr(self, key, dct[key])
class Bar(object):
def __init__(self, dct):
for key in dct:
setattr(self, key, dct[key])
def __getattr__(self, attr):
"""If attr is not in channel, look in timing_data
"""
return getattr(self.foo, attr)
if __name__=='__main__':
dct={'a':1,'b':2,'c':3}
foo=Foo(dct)
dct2={'d':1,'e':2,'f':3,'foo':foo}
bar=Bar(dct2)
pickle.dump(bar,open('test.pkl','w'))
bar=pickle.load(open('test.pkl','r'))
Results:
14 """If attr is not in channel, look in timing_data
15 """
---> 16 return getattr(self.foo, attr)
17
18 if __name__=='__main__':
RuntimeError: maximum recursion depth exceeded while calling a Python object
Python getattr() function is used to get the value of an object's attribute and if no attribute of that object is found, default value is returned.
Python getattr() function is used to access the attribute value of an object and also gives an option of executing the default value in case of unavailability of the key.
__getattribute__ has a default implementation, but __getattr__ does not. This has a clear meaning: since __getattribute__ has a default implementation, while __getattr__ not, clearly python encourages users to implement __getattr__ .
The problem here is that your __getattr__
method is poorly implemented. It assumes that self.foo
exists. If self.foo
doesn't exist, trying to access it ends up calling __getattr__
- which results in infinite recursion:
>>> bar = Bar({}) # no `foo` attribute
>>> bar.x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "untitled.py", line 19, in __getattr__
return getattr(self.foo, attr)
File "untitled.py", line 19, in __getattr__
return getattr(self.foo, attr)
File "untitled.py", line 19, in __getattr__
return getattr(self.foo, attr)
[Previous line repeated 329 more times]
RecursionError: maximum recursion depth exceeded while calling a Python object
To fix this, you have to throw an AttributeError if no foo
attribute exists:
def __getattr__(self, attr):
"""If attr is not in channel, look in timing_data
"""
if 'foo' not in vars(self):
raise AttributeError
return getattr(self.foo, attr)
(I used the vars
function to get the object's dict, because it looks nicer than self.__dict__
.)
Now everything works as expected:
dct={'a':1,'b':2,'c':3}
foo=Foo(dct)
dct2={'d':1,'e':2,'f':3,'foo':foo}
bar=Bar(dct2)
data = pickle.dumps(bar)
bar = pickle.loads(data)
print(vars(bar))
# output:
# {'d': 1, 'e': 2, 'f': 3, 'foo': <__main__.Foo object at 0x7f040fc7e7f0>}
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