I am trying to pickle and unpickle datetime.datetime
subclass object. However, it always yields error and I have no clue why it is and how to solve it. Here is the minimum example:
from datetime import datetime, date, time
class A(datetime):
def __new__(cls, year = 2016, month=1, day=1, hour=0, minute=0, leap_year=False):
return datetime.__new__(cls, year ,month, day, hour, minute)
import pickle
obj = A( month=1, day=1, hour=1, leap_year = False)
serial = pickle.dumps(obj, protocol=pickle.HIGHEST_PROTOCOL)
unpickle = pickle.loads( serial, encoding='bytes')
This will give the following error:
TypeError Traceback (most recent call last)
<ipython-input-2-12195a09d83d> in <module>()
5
6
----> 7 unpickle = pickle.loads( serial, encoding='bytes')
<ipython-input-1-605483566b52> in __new__(cls, year, month, day, hour, minute, leap_year)
2 class A(datetime):
3 def __new__(cls, year = 2016, month=1, day=1, hour=0, minute=0, leap_year=False):
----> 4 return datetime.__new__(cls, year ,month, day, hour, minute)
5
TypeError: an integer is required (got type bytes)
Does anyone know where the problem might be and how to solve it?
I was able to get the following to work based on the information in the Pickling Class Instances section of the pickle
module's documentation. The tuple of values returned from the __reduce_ex__()
method that has been added will cause the __new__()
constructor to be called when the class instance is unpickled by the pickle.loads()
call — just like what normally happens when you call a class.
Note that I didn't need to know whether or how datetime
customizes pickling nor understand its C implementation.
Also note that since the leap_year
argument you had was being ignored by the implementation in your question (and it's unclear why it would need to be passed to the initializer anyway), I've replaced it with a Python property
that computes a boolean value for it dynamically based on the current instance's year value.
from datetime import datetime, date, time
import pickle
class A(datetime):
def __new__(cls, year=2016, month=1, day=1, hour=0, minute=0):
return datetime.__new__(cls, year, month, day, hour, minute)
def __reduce_ex__(self, protocol):
return (type(self), (self.year, self.month, self.day, self.hour, self.minute))
@property
def is_leapyear(self):
''' Determine if specified year is leap year. '''
year = self.year
if year % 4 != 0:
return False
elif year % 100 != 0:
return True
elif year % 400 != 0:
return False
else:
return True
obj = A(month=1, day=1, hour=1, leap_year=False)
print('calling pickle.dumps()')
serial = pickle.dumps(obj, protocol=pickle.HIGHEST_PROTOCOL)
print('calling pickle.loads()')
unpickle = pickle.loads(serial, encoding='bytes')
print('unpickled {!r}'.format(unpickle)) # -> unpickled A(2016, 1, 1, 1, 0)
print('unpickle.is_leapyear: {}'.format(unpickle.is_leapyear)) # -> True