Ant*_*hon 9 python json dictionary casting
我需要创建一个t类似于dict的类的实例,T它支持"转换"到真正的dict dict(**t),而不需要回复
dict([(k, v) for k, v in t.items()]).同时支持使用标准json库转储为JSON ,而不扩展普通的JSON编码器(即没有为default参数提供功能).
随着t作为一个正常的dict,这两个工作:
import json
def dump(data):
print(list(data.items()))
try:
print('cast:', dict(**data))
except Exception as e:
print('ERROR:', e)
try:
print('json:', json.dumps(data))
except Exception as e:
print('ERROR:', e)
t = dict(a=1, b=2)
dump(t)
Run Code Online (Sandbox Code Playgroud)
打印:
[('a', 1), ('b', 2)]
cast: {'a': 1, 'b': 2}
json: {"a": 1, "b": 2}
Run Code Online (Sandbox Code Playgroud)
但是我希望t成为类的一个实例,T例如将一个default "动态"键添加到其项目中,因此不需要预先插入(实际上我希望来自一个或多个T实例的合并键出现,这个是对那个真实的,更复杂的类的简化.
class T(dict):
def __getitem__(self, key):
if key == 'default':
return 'DEFAULT'
return dict.__getitem__(self, key)
def items(self):
for k in dict.keys(self):
yield k, self[k]
yield 'default', self['default']
def keys(self):
for k in dict.keys(self):
yield k
yield 'default'
t = T(a=1, b=2)
dump(t)
Run Code Online (Sandbox Code Playgroud)
这给了:
[('a', 1), ('b', 2), ('default', 'DEFAULT')]
cast: {'a': 1, 'b': 2}
json: {"a": 1, "b": 2, "default": "DEFAULT"}
Run Code Online (Sandbox Code Playgroud)
由于没有关键的"默认",演员阵容无法正常工作,我不知道提供哪种"魔术"功能来进行演员表演.
当我构建实现T的功能collections.abc,并在子类中提供所需的抽象方法时,转换工作:
from collections.abc import MutableMapping
class TIter:
def __init__(self, t):
self.keys = list(t.d.keys()) + ['default']
self.index = 0
def __next__(self):
if self.index == len(self.keys):
raise StopIteration
res = self.keys[self.index]
self.index += 1
return res
class T(MutableMapping):
def __init__(self, **kw):
self.d = dict(**kw)
def __delitem__(self, key):
if key != 'default':
del self.d[key]
def __len__(self):
return len(self.d) + 1
def __setitem__(self, key, v):
if key != 'default':
self.d[key] = v
def __getitem__(self, key):
if key == 'default':
return 'DEFAULT'
# return None
return self.d[key]
def __iter__(self):
return TIter(self)
t = T(a=1, b=2)
dump(t)
Run Code Online (Sandbox Code Playgroud)
这使:
[('a', 1), ('b', 2), ('default', 'DEFAULT')]
cast: {'a': 1, 'b': 2, 'default': 'DEFAULT'}
ERROR: Object of type 'T' is not JSON serializable
Run Code Online (Sandbox Code Playgroud)
JSON转储失败,因为该转储器无法处理
MutableMapping子类,它使用C语言显式测试PyDict_Check.
当我试图让T两者的子类dict和
MutableMapping,我没有得到相同的结果只用时为dict子类.
我当然可以认为这是一个错误,json转储器尚未更新以假设(具体的子类)
collections.abc.Mapping是可转储的.但即使它被认为是一个bug并在未来的Python版本中得到修复,我也不认为这样的修复将适用于旧版本的Python.
问题1:我如何才能使T实现成为dict正确的子类
?
Q2:如果Q1没有答案,如果我创建一个返回正确值的C级别
PyDict_Check但是没有执行任何实际实现(然后创建它T的子类以及MutableMapping(我)不要认为添加这样一个不完整的C级别的dict会起作用,但我还没有尝试过),这个傻瓜会不会这样json.dumps()?
Q3这是一个完全错误的方法,让两者都像第一个例子一样工作吗?
实际的代码,更复杂,是我的ruamel.yaml库的一部分,它必须在Python 2.7和Python 3.4+上工作.
只要我无法解决这个问题,我就必须告诉那些过去使用JSON转储器(没有额外参数)的人:
def json_default(obj):
if isinstance(obj, ruamel.yaml.comments.CommentedMap):
return obj._od
if isinstance(obj, ruamel.yaml.comments.CommentedSeq):
return obj._lst
raise TypeError
print(json.dumps(d, default=json_default))
Run Code Online (Sandbox Code Playgroud)
告诉他们使用与默认(往返)加载器不同的加载器.例如:
yaml = YAML(typ='safe')
data = yaml.load(stream)
Run Code Online (Sandbox Code Playgroud)
,.to_json()在类上实现一些方法T,让用户ruamel.yaml意识到这一点
,或者回到子类化dict并告诉人们去做
dict([(k, v) for k, v in t.items()])
Run Code Online (Sandbox Code Playgroud)
其中没有一个是非常友好的,并且表明不可能创建一个非平凡的类似dict的类,并且与标准库很好地配合.
blh*_*ing -1
您可以用完全不同的方式解决问题。您可以使用设置为所需值'default'的键来初始化字典'default',然后通过覆盖所有可能更改内容的方法来保护键的值,而不是在动态请求键时尝试生成值。'default'字典,以便键的值'default'永远不会改变:
class T(dict):
def __init__(self, **kwargs):
kwargs['default'] = 'DEFAULT'
super(T, self).__init__(**kwargs)
def __setitem__(self, key, value):
if key != 'default':
super(T, self).__setitem__(key, value)
def __delitem__(self, key):
if key != 'default':
super(T, self).__delitem__(key)
def clear(self):
super(T, self).clear()
self.__init__()
def pop(self, key, **kwargs):
if key == 'default':
return self[key]
return super(T, self).pop(key, **kwargs)
def popitem(self):
key, value = super(T, self).popitem()
if key == 'default':
key2, value2 = super(T, self).popitem()
super(T, self).__setitem__(key, value)
return key2, value2
return key, value
def update(self, other, **kwargs):
if kwargs:
if 'default' in kwargs:
del kwargs['default']
elif 'default' in other:
del other['default']
super(T, self).update(other, **kwargs)
Run Code Online (Sandbox Code Playgroud)