【问题标题】:Unable to pickle datetime subclass无法腌制日期时间子类
【发布时间】:2020-01-03 00:09:00
【问题描述】:

我正在尝试腌制和取消腌制datetime.datetime 子类对象。但是,它总是会产生错误,我不知道为什么会这样以及如何解决它。这是最小的例子:

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

这将给出以下错误:

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)

有谁知道问题可能出在哪里以及如何解决?

【问题讨论】:

  • 请务必发布整个错误消息,并附上完整的追溯
  • pickle 模块不使用类的__new__() 方法来重构它的实例。
  • @martineau。很高兴知道。谢谢。那么如果我想使用pickle,我应该避免使用__new__吗?
  • 问题不在于__new__的使用。 (您甚至可以看到您的 __new__ 在回溯中被调用 - 大多数类不会发生这种情况,但由于datetime 自定义酸洗的方式而发生。)问题是datetime 酸洗实现不是t 旨在与子类一起使用(除此之外它完全没有文档记录)。
  • 序列化和子类化都比看起来复杂得多,但是人们被训练期望序列化和子类化都可以毫不费力地工作。把它们放在一起,问题就更大了。

标签: python datetime pickle python-datetime


【解决方案1】:

根据pickle 模块documentationPickling Class Instances 部分中的信息,我能够使以下工作正常进行。从已添加的__reduce_ex__() 方法返回的值元组将导致__new__() 构造函数在pickle.loads() 调用取消选中类实例时被调用——就像调用类时通常发生的情况一样。

请注意,我不需要知道 datetime 是否或如何自定义酸洗,也不需要了解它的 C 实现。

还请注意,由于您的问题中的实现忽略了 leap_year 参数(而且尚不清楚为什么需要将其传递给初始化程序),我已将其替换为 Python @987654329 @ 根据当前实例的年份值动态计算布尔值。

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

【讨论】:

  • 亲爱的@martineau,非常感谢您的帮助,这正是我想要的!现在一切正常!谢谢!
  • 关于leap_year 参数:我在其他一些实现中使用它,我在提供最小示例时忘记删除它。无论如何,您在上面提供的内容也有效!非常感谢。
  • bilibili:我想知道它为什么会在那里。如果您不需要它,只需将那部分放在一边。无论如何,我很高兴听到您对我的回答表示赞赏(并且了解到 Python 的文档实际上有多好 ;¬)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-01-01
  • 2016-10-12
  • 2018-12-14
  • 1970-01-01
  • 2014-01-16
  • 2020-01-06
  • 1970-01-01
相关资源
最近更新 更多