Python3.5对象和json.dumps()输出

时间:2016-03-23 15:09:15

标签: python json class datetime python-3.5

我写了一个类,允许我将日期(整数)添加到日期(字符串%Y-%m-%d)。此类的对象需要是JSON可序列化的。

以整数形式向我的对象添加天数按预期工作。但是json.dumps(obj)为我的原始对象返回了太多信息(" 2016-03-23 15:57:47.926362")。 为什么?我如何修改课程以获得"" 2016-03-23"相反?请参阅下面的示例。

代码:

from datetime import datetime, timedelta
import json

class Day(str):
    def __init__(self, _datetime):
        self.day = _datetime

    def __str__(self):
        return self.day.date().isoformat()

    def __repr__(self):
        return "%s" % self.day.date().isoformat()

    def __add__(self, day):
        new_day = self.day + timedelta(days=day)
        return Day(new_day).__str__()

    def __sub__(self, day):
        new_day = self.day - timedelta(days=day)
        return Day(new_day).__str__()


if __name__ == "__main__":
    today = Day(datetime.today())
    print(today)               # 2016-03-23
    print(json.dumps(today))   # "2016-03-23 15:57:47.926362"
    print(today+1)             # 2016-03-24
    print(json.dumps(today+1)) # "2016-03-24"
    print(today-1)             # 2016-03-22
    print(json.dumps(today-1)) # "2016-03-22"

更新即可。这是我感兴趣的最终代码:

from datetime import datetime, timedelta
import json


class Day(str):
    def __init__(self, datetime_obj):
        self.day = datetime_obj

    def __new__(self, datetime):
        return str.__new__(Day, datetime.date().isoformat())

    def __add__(self, day):
        new_day = self.day + timedelta(days=day)
        return Day(new_day)

    def __sub__(self, day):
        new_day = self.day - timedelta(days=day)
        return Day(new_day)


if __name__ == "__main__":
    today = Day(datetime.today())
    print(type(today))
    print(today)  # 2016-03-23
    print(json.dumps(today))  # "2016-03-23"
    print(today + 1)  # 2016-03-24
    print(json.dumps(today + 1))  # "2016-03-24"
    print(today - 1)  # 2016-03-22
    print(json.dumps(today - 1))  # "2016-03-22"
    print(json.dumps(dict(today=today))) # {"today": "2016-03-23"}
    print(json.dumps(dict(next_year=today+365))) # {"next_year": "2017-03-23"}
    print(json.dumps(dict(last_year=today-366))) # {"last_year": "2015-03-23"}

2 个答案:

答案 0 :(得分:4)

酷!让我们一起去吧。你看到了:

print(json.dumps(today))   # "2016-03-23 15:57:47.926362"

由于编码过程中somewhere,在决定如何序列化传递给它的内容时,json.dumps会在您的对象上调用isinstance(..., str)。这将返回True,并且您的对象被秘密地序列化为此字符串。

"2016-03-23 15:57:47.926362"值来自哪里?

当你致电day = Day(datetime_obj)时,会发生两件事:

  • __new__ is called实例化对象。您尚未提供__new__方法,因此使用了str.__new__
  • __init__被调用初始化对象。

所以day = Day(datetime_obj)有效地转换为:

day = str.__new__(Day, datetime_obj)

对于json.dumps,您的对象将是str,但str的值设置为datetime_obj的默认字符串表示形式。这恰好是您看到的完整格式。内建,伙计!

我玩弄了这个,看来如果你滚动自己的__new__(这是一个令人兴奋的领域,小心翼翼),它拦截str.__new__电话,你~~应该好〜 :

class Day(str):
    def __new__(self, datetime):
        return str.__new__(Day, datetime.date().isoformat())

但是,如果整件事发生火灾,你就没有听到我的消息。

PS 正确would be子类JSONEncoder的方式。但它没有乐趣。

PS2 哦,拍摄,我在2.7进行了测试。我可能完全在那里,如果我在,那就给我一个"you tried"徽章。

答案 1 :(得分:1)

json.dumps(today)行为的原因并不像第一眼看上去那么明显。要理解这个问题,您应该能够回答两个问题:

  • 包含时间的字符串值来自哪里?
  • 为什么json编码器不调用Day.__str__?应该吗?

这里有一些先决条件:

  1. datetime.today()方法类似于datetime.now() - 它包括当前时间(小时,分钟等)。您可以使用date.today(),仅获取日期。

  2. str在Python中创建不可变对象;其值在您未覆盖的__new__方法中设置,因此默认转化str(datetime.today())用于将Day的值初始化为字符串。它会创建包含日期和时间的字符串值。您可以覆盖__new__,以获得不同的字符串值

    def __new__(cls, _datetime):
        return str.__new__(cls, _datetime.date())
    
  3. Daystr子类,因此其实例编码为JSON字符串

  4. str方法返回str个对象而不是相应的子类对象,除非你覆盖它们,例如:

    >>> class S(str):
    ...    def duplicate(self):
    ...        return S(self * 2)
    ...
    >>> s = S('abc')
    >>> s.duplicate().duplicate()
    'abcabcabcabc'
    >>> s.upper().duplicate()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'str' object has no attribute 'duplicate'
    

    s.upper()在此处返回str个对象而不是S,并且以下.duplicate()调用失败。

  5. 在您的情况下,要创建相应的JSON字符串,json.dumps(today)会在re.sub()对象上执行操作(json.encode.encode_basestring()调用),该对象将其值用作字符串即问题是,today子类的实例上re.sub()encode_basestring()都没有调用__str__()方法。即使strencode_basestring(s)一样简单;结果将是相同的:return '"' + s + '"'返回'"' + today个对象,并且不会调用str

    我不知道Day.__str__模块是否应该在接受re的函数中调用str(obj)。或者isinstance(obj, str)是否应该这样做(或两者都没有。)

    如果你无法修复json.encode.encode_basestring()课程;您可以修补Day来调用json.encode.encode_basestring(),以获得str(obj)子类型实例的理想JSON表示 if < / strong>你想获得str方法返回的值 - 抛开是否明智地首先覆盖__str__()子类上的__str__() ):

    str

    相关Python问题:Cannot override JSON encoding of basic type subclasses