Python 如何让json.dumps将我的类视为dict?

Python 如何让json.dumps将我的类视为dict?,python,json,dictionary,duck-typing,Python,Json,Dictionary,Duck Typing,我想创建一个自定义的Python类,JSON像dict一样序列化。以Python的duck类型命名,我想我可以创建一个看起来和dict完全一样的类。但是,下面显示的类显然不适合于json.dumps——下面的代码产生错误TypeError:totalyadict类型的对象不是json可序列化的。我可以对totalyadict做哪些更改,以便json.dumps的默认编码器将输出{“a”:1,“b”:2,“c”:3} 我知道这个直接的问题可以通过创建一个自定义编码器来解决,但这在这个具体问题衍生出

我想创建一个自定义的Python类,JSON像dict一样序列化。以Python的duck类型命名,我想我可以创建一个看起来和dict完全一样的类。但是,下面显示的类显然不适合于
json.dumps
——下面的代码产生错误
TypeError:totalyadict类型的对象不是json可序列化的
。我可以对totalyadict做哪些更改,以便
json.dumps
的默认编码器将输出
{“a”:1,“b”:2,“c”:3}

我知道这个直接的问题可以通过创建一个自定义编码器来解决,但这在这个具体问题衍生出来的更大问题中不是一个可接受的解决方案

另一个尝试性的解决方案是让TotalyaDict从dict继承,而不是MutableMapping。这不会引发任何异常,但在这种情况下,
json.dumps(x)
会产生
{}
;显然,
json.dumps
的默认编码器用于dicts的数据源不是下面的任何重写方法

这里我想要的是能够使用属性语义(
x.c=x.a+x.b
),但仍然可以序列化为JSON对象。因此,一个似乎不起作用的可能建议是TypedDict(必须是
x['c']=x['a']+x['b']
)。通过
\uuuu setattr\uuuuuu
\uuuu getattribute\uuuuuu
截取属性分配和检索,并重定向到从
dict
继承的条目
self
似乎工作得很好,所以这是我的默认解决方案。但是我很惊讶有一次我真的想使用duck类型而不是严格的(ish)类型,它似乎不起作用

from collections.abc导入可变映射
导入json
类totalyadict(可变映射):
定义初始化(self,a,b,c):
self.a=a
self.b=b
self.c=c
self.\u字段={'a','b','c'}
def _u获取项目(自身,密钥):
如果输入self.\u字段:
返回getattr(self,key)
其他:
raise KeyError(“{}”不是{}格式的字段(键,类型(self)。\uuuu name\uuuu))
定义设置项(自身、键、值):
如果输入self.\u字段:
setattr(自身、键、值)
其他:
raise KeyError(“{}”不是{}格式的字段(键,类型(self)。\uuuu name\uuuu))
def uu delitem uu(self,key):
raise RUNTIMERROR('无法从{}中删除字段。'格式(类型(self)。\uuuuu name\uuuuuuuu))
定义(自我):
返回iter(自身字段)
定义(自我):
返回len(self.\u字段)
def__包含__(self,k):
在self.\u字段中返回k
def副本(自我):
返回类型(self)(**{k:getattr(self,k)表示self.\u字段中的k})
定义报告(自我):
返回“{'+',”.join(“{}”):{}.self._字段中k的格式(k,repr(getattr(self,k))+'}'
def get(自身、密钥、默认值=无):
如果输入self.\u字段:
value=getattr(self,key)
如果值为“无”:
值=默认值
返回值
其他:
raise KeyError(“{}”不是{}格式的字段(键,类型(self)。\uuuu name\uuuu))
def setdefault(自身、密钥、默认值=无):
如果输入self.\u字段:
value=getattr(self,key)
如果值为“无”:
值=默认值
setattr(自身、键、值)
返回值
其他:
raise KeyError(“{}”不是{}格式的字段(键,类型(self)。\uuuu name\uuuu))
def pop(自身、键、值=无):
raise RUNTIMERROR('无法从{}中删除字段。'格式(类型(self)。\uuuuu name\uuuuuuuu))
def键(自):
返回self.\u字段
def项目(自身):
在self.\u字段中为k返回[(k,getattr(self,k)]]
def值(自身):
返回[getattr(self,k)表示self.\u字段中的k]
定义(自身、其他):
如果类型(自身)为类型(其他):
对于self.\u字段中的k:
如果getattr(self,k)!=getattr(其他,k):
返回错误
返回真值
其他:
返回错误
定义(自身、其他):
返回非自我。均衡(其他)
x=总偏差(1,2,3)
打印(json.dumps(x))

在某些情况下,最简单的解决方案是最好的。在本例中,创建一个
to_dict()
函数,在json转储自定义类之前,将其内部的数据作为Python字典返回

这样,您可以在空闲时操作类中的数据,并在其他库需要字典时将其转换为字典。如果需要相反的方法,只需编写另一个函数,将dict解析到自定义类中即可

因为这个类是用来保存数据的,所以我建议使用

然后,您可以将此函数添加到类中,以获取其作为dict的属性:

from dataclasses import dataclass, asdict

def get_as_dict(self):
        return {k: v for k, v in asdict(self).items() if self._dataclass_fields_[k].repr}

这里的问题是您的
\u字段
变量。这不会序列化为JSON对象,因为
{'c','b','a'}
不是有效的JSON。如果查看
x.\uuu dict\uuu
属性,可以看到此对象将表示为什么

{'a': 1, 'b': 2, 'c': 3, '_fields': {'c', 'b', 'a'}}
如果将
\u字段
更改为列表,还可以使用JSON.dumps中的
默认
参数

这些是我所做的改变,以使你所期待的工作

self._fields = ['a', 'b', 'c']
print(json.dumps(x, default=vars))
这是我的全部密码

from collections.abc import MutableMapping
import json


class TotallyADict(MutableMapping):
  def __init__(self, a, b, c):
    self.a = a
    self.b = b
    self.c = c
    self._fields = ['a', 'b', 'c']

  def __getitem__(self, key):
    if key in self._fields:
      return getattr(self, key)
    else:
      raise KeyError('"{}" is not a field in {}'.format(key, type(self).__name__))

  def __setitem__(self, key, value):
    if key in self._fields:
      setattr(self, key, value)
    else:
      raise KeyError('"{}" is not a field in {}'.format(key, type(self).__name__))

  def __delitem__(self, key):
    raise RuntimeError('Cannot delete fields from {}'.format(type(self).__name__))

  def __iter__(self):
    return iter(self._fields)

  def __len__(self):
    return len(self._fields)

  def __contains__(self, k):
    return k in self._fields

  def copy(self):
    return type(self)(**{k: getattr(self, k) for k in self._fields})

  def __repr__(self):
    return '{' + ', '.join('"{}": {}'.format(k, repr(getattr(self, k))) for k in self._fields) + '}'

  def get(self, key, default=None):
    if key in self._fields:
      value = getattr(self, key)
      if value is None:
        value = default
      return value
    else:
      raise KeyError('"{}" is not a field in {}'.format(key, type(self).__name__))

  def setdefault(self, key, default=None):
    if key in self._fields:
      value = getattr(self, key)
      if value is None:
        value = default
        setattr(self, key, value)
      return value
    else:
      raise KeyError('"{}" is not a field in {}'.format(key, type(self).__name__))

  def pop(self, key, value=None):
    raise RuntimeError('Cannot delete fields from {}'.format(type(self).__name__))

  def keys(self):
    return self._fields

  def items(self):
    return [(k, getattr(self, k)) for k in self._fields]

  def values(self):
    return [getattr(self, k) for k in self._fields]

  def __eq__(self, other):
    if type(self) is type(other):
      for k in self._fields:
        if getattr(self, k) != getattr(other, k):
          return False
      return True
    else:
      return False

  def __ne__(self, other):
    return not self.__eq__(other)


x = TotallyADict(1, 2, 3)

print(json.dumps(x, default=vars))
您也可以尝试使用
UserDict


在阅读了JSON的文档之后,您可能缺少了
default
方法Yes,调用
JSON.dumps(x,default=lambda d:{k:d[k]表示d中的k.\u fields})
或类似方法会产生所需的结果。然而,这并不能回答为什么
totalyadict
json.dumps
,或者如何更改它,使其将duck type作为
dict
的问题。根据问题的第一段,所需的输出是
{“a”:1,“b”:2,“c”:3}
,但这种方法输出
{“a”:1,“b”:2,“c”:3,“_字段”:[“a”、“b”、“c”]}
。没有理由在这里使用
dataclass
,因为您的
get_as dict
可以简化为
返回{k:self[k]表示self中的k。_字段}
没有它,但是