Python3.5 对象和 json.dumps() 输出
Python3.5 object and json.dumps() output
我写了一个 class,它允许我将天数(整数)添加到日期(字符串 %Y-%m-%d)。此 class 的对象需要 JSON 可序列化。
以整数形式向我的对象添加天数按预期工作。但是 json.dumps(obj) returns 我的原始对象的信息太多(“2016-03-23 15:57:47.926362”)。 为什么?我需要如何修改 class 以获取“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"}
酷!让我们一起去吧。您正在查看:
print(json.dumps(today)) # "2016-03-23 15:57:47.926362"
因为 somewhere 在编码过程中,在决定如何序列化传递给它的内容时,json.dumps
在您的对象上调用 isinstance(..., str)
。这个 returns 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" 徽章。
json.dumps(today)
行为的原因并不像乍看起来那么明显。要理解这个问题,您应该能够回答两个问题:
- 包含时间的字符串值从哪里来?
- 为什么
Day.__str__
不被 json 编码器调用?应该吗?
这里有一些先决条件:
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
subclass 因此它的实例被编码为 JSON strings
str
方法 return str
对象而不是相应的 subclass 对象,除非你覆盖它们例如:
>>> 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()
returns str
对象而不是 S
并且下面的 .duplicate()
调用失败。
在您的例子中,要创建相应的 JSON 字符串,json.dumps(today)
对 today
对象执行操作(re.sub()
在 json.encode.encode_basestring()
中调用)将其值用作字符串,即,问题是 re.sub()
和 encode_basestring()
都没有在 str
subclass 实例上调用 __str__()
方法。即使 encode_basestring(s)
和 return '"' + s + '"'
一样简单;结果是一样的:'"' + today
return 是一个 str
对象并且 Day.__str__
没有被调用。
我不知道 re
模块是否应该在接受 isinstance(obj, str)
的函数中调用 str(obj)
。或者 json.encode.encode_basestring()
是否应该这样做(或两者都不做)。
如果无法修复Day
class;您可以 修补 json.encode.encode_basestring()
以调用 str(obj)
,以获得 str
子类型实例 的理想 JSON 表示( 如果你想通过__str__()
方法得到值return——暂且不考虑覆盖是否明智__str__()
在第一个 str
子 class 上 ):
import json
for suffix in ['', '_ascii']:
function_name = 'encode_basestring' + suffix
orig_function = getattr(json.encoder, function_name)
setattr(json.encoder, function_name, lambda s,_e=orig_function: _e(str(s)))
相关 Python 问题:Cannot override JSON encoding of basic type subclasses
我写了一个 class,它允许我将天数(整数)添加到日期(字符串 %Y-%m-%d)。此 class 的对象需要 JSON 可序列化。
以整数形式向我的对象添加天数按预期工作。但是 json.dumps(obj) returns 我的原始对象的信息太多(“2016-03-23 15:57:47.926362”)。 为什么?我需要如何修改 class 以获取“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"}
酷!让我们一起去吧。您正在查看:
print(json.dumps(today)) # "2016-03-23 15:57:47.926362"
因为 somewhere 在编码过程中,在决定如何序列化传递给它的内容时,json.dumps
在您的对象上调用 isinstance(..., str)
。这个 returns 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" 徽章。
json.dumps(today)
行为的原因并不像乍看起来那么明显。要理解这个问题,您应该能够回答两个问题:
- 包含时间的字符串值从哪里来?
- 为什么
Day.__str__
不被 json 编码器调用?应该吗?
这里有一些先决条件:
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
subclass 因此它的实例被编码为 JSON stringsstr
方法 returnstr
对象而不是相应的 subclass 对象,除非你覆盖它们例如:>>> 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()
returnsstr
对象而不是S
并且下面的.duplicate()
调用失败。
在您的例子中,要创建相应的 JSON 字符串,json.dumps(today)
对 today
对象执行操作(re.sub()
在 json.encode.encode_basestring()
中调用)将其值用作字符串,即,问题是 re.sub()
和 encode_basestring()
都没有在 str
subclass 实例上调用 __str__()
方法。即使 encode_basestring(s)
和 return '"' + s + '"'
一样简单;结果是一样的:'"' + today
return 是一个 str
对象并且 Day.__str__
没有被调用。
我不知道 re
模块是否应该在接受 isinstance(obj, str)
的函数中调用 str(obj)
。或者 json.encode.encode_basestring()
是否应该这样做(或两者都不做)。
如果无法修复Day
class;您可以 修补 json.encode.encode_basestring()
以调用 str(obj)
,以获得 str
子类型实例 的理想 JSON 表示( 如果你想通过__str__()
方法得到值return——暂且不考虑覆盖是否明智__str__()
在第一个 str
子 class 上 ):
import json
for suffix in ['', '_ascii']:
function_name = 'encode_basestring' + suffix
orig_function = getattr(json.encoder, function_name)
setattr(json.encoder, function_name, lambda s,_e=orig_function: _e(str(s)))
相关 Python 问题:Cannot override JSON encoding of basic type subclasses