Хотя вы можете дать своему подклассу метод __new__
, который обнаруживает один экземпляр datetime.datetime
, а затем выполняет все копирование там, я бы фактически дал классу метод класса, просто чтобы обработать этот случай, чтобы ваш код SQLAlchemy выглядел так как:
return SerializableDateTime.from_datetime(value)
Мы можем использовать поддержку pickle
, которую класс datetime.datetime()
уже реализует; типы реализуют хук __reduce_ex__
(обычно на основе методов более высокого уровня, таких как __getnewargs__
), а для экземпляров datetime.datetime()
этот хук возвращает только тип datetime.datetime
и args
, что означает, что, если у вас есть подкласс с тем же внутренним состоянием, мы можем создать новую копию с тем же состоянием, применив кортеж args
к вашему новому типу. Метод __reduce_ex__
может варьировать выходные данные в соответствии с протоколом маринования, но если вы передадите pickle.HIGHEST_PROTOCOL
, вы гарантированно получите полный поддерживаемый диапазон значений.
Кортеж args
состоит из одного или двух значений, вторым является часовой пояс:
>>> from pickle import HIGHEST_PROTOCOL
>>> value = datetime.now()
>>> value.__reduce_ex__(HIGHEST_PROTOCOL)
(<class 'datetime.datetime'>, (b'\x07\xe2\n\x1f\x12\x06\x05\rd\x8f',))
>>> datetime.utcnow().astimezone(timezone.utc).__reduce_ex__(value.__reduce_ex__(HIGHEST_PROTOCOL))
(<class 'datetime.datetime'>, (b'\x07\xe2\n\x1f\x12\x08\x14\n\xccH', datetime.timezone.utc))
Это первое значение в кортеже args
представляет собой значение bytes
, которое представляет все атрибуты объекта (за исключением часового пояса), и конструктор для datetime
принимает это же значение в байтах (плюс необязательный часовой пояс):
>>> datetime(b'\x07\xe2\n\x1f\x12\x06\x05\rd\x8f') == value
True
Поскольку ваш подкласс принимает те же аргументы, вы можете использовать кортеж args
для создания копии; мы можем использовать первое значение для защиты от изменений в будущих версиях Python, утверждая, что он все еще является нашим родительским классом:
from pickle import HIGHEST_PROTOCOL
class SerializableDateTime(datetime):
@classmethod
def from_datetime(cls, dt):
"""Create a SerializableDateTime instance from a datetime.datetime object"""
# (ab)use datetime pickle support to copy state across
factory, args = dt.__reduce_ex__(HIGHEST_PROTOCOL)
assert issubclass(cls, factory)
return cls(*args)
def serialize(self):
return self.strftime('%Y-%m-%d %H:%M')
Это позволяет вам создавать экземпляры вашего подкласса как копию:
>>> SerializableDateTime.from_datetime(datetime.now())
SerializableDateTime(2018, 10, 31, 18, 13, 2, 617875)
>>> SerializableDateTime.from_datetime(datetime.utcnow().astimezone(timezone.utc))
SerializableDateTime(2018, 10, 31, 18, 13, 22, 782185, tzinfo=datetime.timezone.utc)
Хотя использование хука pickle __reduce_ex__
может показаться несколько хакерским, именно этот протокол используется для создания копий datetime.datetime
экземпляров с модулем copy
, а также с использованием __reduce_ex__(HIGHEST_PROTOCOL)
Вы гарантируете, что все соответствующие состояния копируются независимо от используемой версии Python.