可以从现有日期时间实例创建的自定义日期时间子类?
Custom datetime subclass that can be created from an existing datetime instance?
我需要一种方法来轻松创建 datetime.datetime
subclass 的实例,给定一个现有的 datetime.datetime()
实例。
假设我有以下人为的例子:
class SerializableDateTime(datetime):
def serialize(self):
return self.strftime('%Y-%m-%d %H:%M')
我正在使用这样的 class(但有点复杂),用于 SQLAlchemy 模型;您可以告诉 SQLAlchemy 使用 TypeDecorator
class 将自定义 class 映射到支持的 DateTime
列值;例如:
class MyDateTime(types.TypeDecorator):
impl = types.DateTime
def process_bind_param(self, value, dialect):
# from custom type to the SQLAlchemy type compatible with impl
# a datetime subclass is fine here, no need to convert
return value
def process_result_value(self, value, dialect):
# from SQLAlchemy type to custom type
# is there a way have this work without accessing a lot of attributes each time?
return SerializableDateTime(value) # doesn't work
我不能在这里使用 return SerializableDateTime(value)
因为默认的 datetime.datetime.__new__()
方法不接受 datetime.datetime()
实例:
>>> value = datetime.now()
>>> SerializableDateTime(value)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: an integer is required (got type datetime.datetime)
是否有一种快捷方式可以避免将 value.year
、value.month
等一直复制到构造函数中的时区?
虽然你可以给你的 subclass 一个 __new__
方法来检测单个 datetime.datetime
实例然后在那里进行所有复制,但我实际上会给 class 一个 class 方法来处理这种情况,所以你的 SQLAlchemy 代码看起来像:
return SerializableDateTime.from_datetime(value)
我们可以利用 pickle
支持 datetime.datetime()
class 已经实现;类型实现 __reduce_ex__
hook (usually building on higher-level methods like __getnewargs__
), and for datetime.datetime()
instances this hook returns just the datetime.datetime
type and an args
tuple, meaning that as long as you have a subclass with the same internal state we can create a new copy with the same state by applying the args
tuple back to your new type. The __reduce_ex__
method can vary output by pickle protocol, but as long as you pass in 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
因为你的 subclass 接受相同的参数,你可以使用 args
元组来创建一个副本;我们可以使用第一个值来防止未来 Python 版本的变化,方法是断言它仍然是我们的父 class:
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')
这样您就可以创建子class的实例作为副本:
>>> 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__
挂钩可能看起来有些 hackish,但这是用于创建 datetime.datetime
实例副本以及 copy
module 的实际协议,并且通过使用 __reduce_ex__(HIGHEST_PROTOCOL)
您确保复制所有相关状态,无论您使用的是什么 Python 版本。
我需要一种方法来轻松创建 datetime.datetime
subclass 的实例,给定一个现有的 datetime.datetime()
实例。
假设我有以下人为的例子:
class SerializableDateTime(datetime):
def serialize(self):
return self.strftime('%Y-%m-%d %H:%M')
我正在使用这样的 class(但有点复杂),用于 SQLAlchemy 模型;您可以告诉 SQLAlchemy 使用 TypeDecorator
class 将自定义 class 映射到支持的 DateTime
列值;例如:
class MyDateTime(types.TypeDecorator):
impl = types.DateTime
def process_bind_param(self, value, dialect):
# from custom type to the SQLAlchemy type compatible with impl
# a datetime subclass is fine here, no need to convert
return value
def process_result_value(self, value, dialect):
# from SQLAlchemy type to custom type
# is there a way have this work without accessing a lot of attributes each time?
return SerializableDateTime(value) # doesn't work
我不能在这里使用 return SerializableDateTime(value)
因为默认的 datetime.datetime.__new__()
方法不接受 datetime.datetime()
实例:
>>> value = datetime.now()
>>> SerializableDateTime(value)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: an integer is required (got type datetime.datetime)
是否有一种快捷方式可以避免将 value.year
、value.month
等一直复制到构造函数中的时区?
虽然你可以给你的 subclass 一个 __new__
方法来检测单个 datetime.datetime
实例然后在那里进行所有复制,但我实际上会给 class 一个 class 方法来处理这种情况,所以你的 SQLAlchemy 代码看起来像:
return SerializableDateTime.from_datetime(value)
我们可以利用 pickle
支持 datetime.datetime()
class 已经实现;类型实现 __reduce_ex__
hook (usually building on higher-level methods like __getnewargs__
), and for datetime.datetime()
instances this hook returns just the datetime.datetime
type and an args
tuple, meaning that as long as you have a subclass with the same internal state we can create a new copy with the same state by applying the args
tuple back to your new type. The __reduce_ex__
method can vary output by pickle protocol, but as long as you pass in 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
因为你的 subclass 接受相同的参数,你可以使用 args
元组来创建一个副本;我们可以使用第一个值来防止未来 Python 版本的变化,方法是断言它仍然是我们的父 class:
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')
这样您就可以创建子class的实例作为副本:
>>> 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__
挂钩可能看起来有些 hackish,但这是用于创建 datetime.datetime
实例副本以及 copy
module 的实际协议,并且通过使用 __reduce_ex__(HIGHEST_PROTOCOL)
您确保复制所有相关状态,无论您使用的是什么 Python 版本。