我写了一个类,允许我将日期(整数)添加到日期(字符串%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"}
答案 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)
行为的原因并不像第一眼看上去那么明显。要理解这个问题,您应该能够回答两个问题:
Day.__str__
?应该吗?这里有一些先决条件:
datetime.today()
方法类似于datetime.now()
- 它包括当前时间(小时,分钟等)。您可以使用date.today()
,仅获取日期。
str
在Python中创建不可变对象;其值在您未覆盖的__new__
方法中设置,因此默认转化str(datetime.today())
用于将Day
的值初始化为字符串。它会创建包含日期和时间的字符串值。您可以覆盖__new__
,以获得不同的字符串值:
def __new__(cls, _datetime):
return str.__new__(cls, _datetime.date())
Day
是str
子类,因此其实例编码为JSON字符串
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()
调用失败。
在您的情况下,要创建相应的JSON字符串,json.dumps(today)
会在re.sub()
对象上执行操作(json.encode.encode_basestring()
调用),该对象将其值用作字符串即问题是,today
子类的实例上re.sub()
和encode_basestring()
都没有调用__str__()
方法。即使str
与encode_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