代码之家  ›  专栏  ›  技术社区  ›  Oleh Rybalchenko

可以从现有datetime实例创建的自定义datetime子类?

  •  3
  • Oleh Rybalchenko  · 技术社区  · 7 年前

    我需要一个方法来轻松地创建 datetime.datetime datetime.datetime() 实例。

    假设我有以下人为的例子:

    class SerializableDateTime(datetime):
        def serialize(self):
            return self.strftime('%Y-%m-%d %H:%M')
    

    我正在使用这样一个类(但有点复杂),用于SQLAlchemy模型;您可以告诉SQLAlchemy将自定义类映射到支持的 DateTime TypeDecorator class ; 例如。:

    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 等等,一直到时区,变成一个构造器?

    1 回复  |  直到 7 年前
        1
  •  5
  •   Martijn Pieters    7 年前

    尽管你可以给你的子类一个 __new__ datetime.datetime 实例然后在那里进行所有复制,实际上我会给类一个classmethod来处理这种情况,所以您的SQLAlchemy代码如下所示:

    return SerializableDateTime.from_datetime(value)
    

    我们可以利用 pickle 支持 datetime.datetime() 类已实现;类型实现 __reduce_ex__ hook __getnewargs__ ),和 datetime.datetime() 实例这个钩子只返回 日期时间。日期时间 类型和an args tuple,意思是只要有一个子类具有相同的内部状态,我们就可以通过应用 回到你的新类型。这个 pickle.HIGHEST_PROTOCOL 您一定会得到支持的全部值范围。

    这个

    >>> 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))
    

    参数 元组是一个 bytes 值,该值表示对象的所有属性(时区除外)和的构造函数 datetime 接受相同的字节值(加上可选时区):

    >>> datetime(b'\x07\xe2\n\x1f\x12\x06\x05\rd\x8f') == value
    True
    

    因为子类接受相同的参数,所以可以使用 参数

    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)
    

    用泡菜的时候 __减少__ hook看起来有点像黑客,这是用于创建 实例与 copy module __reduce_ex__(HIGHEST_PROTOCOL) 您可以确保所有相关的状态都被复制到您所使用的Python版本中。