I defined a Python3 class and then used pickle to serialize and save an instance to file. Later I added another instance attribute to my class, but I realized that if I load my instance and try to reference that attribute I will get an "Object has no attribute" error since the instance was constructed without it. What are the best options for adding the new attribute to my pickled object(s) and configuring it?
In code, I defined a class like
# First definition
class Foo:
def __init__(self, params):
# define and initialize attributes
def print_number(self):
print(2)
I create and serialize an instance using pickle, and save it to file
import pickle
inst = Foo(params)
with open("filename", 'wb') as f:
pickle.dump(inst, f)
Then I want my class to behave a bit differently, so I update its definition:
# Updated definition
class Foo:
def __init__(self, params):
# define and initialize attributes
self.bar = "baz" # bar is a new attribute
def print_number(self):
print(3) # prints 3 instead of 2
Then I load my instance and try to call some methods
import pickle
with open("filename", 'rb') as f:
inst = pickle.load(f)
inst.print_number()
print(inst.bar)
Since pickle doesn't save method definitions, the instance method's behaviour is updated so inst.print_number() prints 3 instead of 2. However the reference inst.bar results in an "Object has no attribute" error because inst was initialized before Foo had that attribute in its definition.
Update
This was a bit of a noob question on my part, I didn't realize that Python lets you just do something like inst.bar = "baz" and set things dynamically (I'm coming from a Java background where everything has to be fixed from the start). I am still interested in hearing about ways to do this properly and/or Pythonicaly and/or pickle-specificly, especially when multiple class updates can be expected.
You could use class inheritance to add new methods/attributes to an existing class:
# First definition
class Foo:
def __init__(self, params):
self.params = params
def print_number(self):
print(2)
import pickle
inst = Foo('params')
with open("filename", 'wb') as f:
pickle.dump(inst, f)
del inst
# Updated definition
class Foo(Foo):
def __init__(self, params):
super().__init__(params)
self.bar = "baz" # bar is a new attribute
def print_number(self):
print(3)
with open("filename", 'rb') as f:
inst = Foo(pickle.load(f))
inst.print_number()
print(inst.bar)
# Outputs:
# 3
# baz
Or it probably makes more sense in practice to do something like this:
with open("filename", 'rb') as f:
inst = pickle.load(f)
# Updated definition
class Foo(inst.__class__):
def __init__(self, params):
super().__init__(params)
self.bar = "baz" # bar is a new attribute
def print_number(self):
print(3)
inst = Foo(inst)
inst.print_number()
print(inst.bar)
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