【问题标题】:python: behavior of json.dumps on dictpython: json.dumps 在 dict 上的行为
【发布时间】:2014-01-18 06:44:48
【问题描述】:

我正在尝试覆盖dictjson.dumps 上的行为。例如,我可以订购钥匙。因此,我创建了一个继承 if dict 的类,并覆盖了它的一些方法。

import json

class A(dict):
    def __iter__(self):
        for i in range(10):
            yield i
    def __getitem__(self, name):
        return None

print json.dumps(A())

但它不调用我的任何方法,只给我{}

有一种方法可以给我正确的行为:

import json

class A(dict):
    def __init__(self):
        dict.__init__(self, {None:None})
    def __iter__(self):
        for i in range(10):
            yield i
    def __getitem__(self, name):
        return None

print json.dumps(A())

Wich 终于给了{"0": null, "1": null, "2": null, "3": null, "4": null, "5": null, "6": null, "7": null, "8": null, "9": null}

因此,很明显json.dumps 的C 实现以某种方式测试dict 是否为空。不幸的是,我无法弄清楚调用了哪个方法。首先,__getattribute__ 不起作用,其次我已经覆盖了 dict 定义或可能定义但没有成功的所有方法。

那么,有人可以向我解释一下 json.dumps 的 C 实现如何检查 dict 是否为空,有没有办法覆盖它(我觉得我的 __init__ 很丑)。

谢谢。

编辑:

我终于找到了这个在 C 代码中的附加位置,而且看起来不能自定义

_json.c 第 2083 行:

if (open_dict == NULL || close_dict == NULL || empty_dict == NULL) {
    open_dict = PyString_InternFromString("{");
    close_dict = PyString_InternFromString("}");
    empty_dict = PyString_InternFromString("{}");
    if (open_dict == NULL || close_dict == NULL || empty_dict == NULL)
        return -1;
}
if (Py_SIZE(dct) == 0)
    return PyList_Append(rval, empty_dict);

所以看起来Py_SIZE 用于检查dict 是否为空。但这是一个宏(不是函数),它只返回python对象的一个​​属性。

object.h 第 114 行:

#define Py_REFCNT(ob)           (((PyObject*)(ob))->ob_refcnt)
#define Py_TYPE(ob)             (((PyObject*)(ob))->ob_type)
#define Py_SIZE(ob)             (((PyVarObject*)(ob))->ob_size)

因此,由于它不是函数,因此无法覆盖,因此无法自定义其行为。

最后,如果想要通过继承 dict 来自定义 json.dumps,则“非空 dict 技巧”是必要的(当然其他方法也可以实现)。

【问题讨论】:

  • 不,不使用__len__

标签: python json python-2.7 dictionary


【解决方案1】:

修改编码器的行为比创建新的 dict 子类更容易吗?

class OrderedDictJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if hasattr(obj, 'keys'):
            return {} # replace your unordered dict with an OrderedDict from collections
        else:
            return super(OrderedDictJSONEncoder, self).default(obj)

并像这样使用它:

json.dumps(my_dict_to_encode, cls=OrderedDictJSONEncoder)

这似乎是将无序 Python 字典转换为有序 JSON 对象的正确位置。

【讨论】:

  • 当然可以,但我希望它尽可能透明。而且由于我已经找到了解决方案,所以问题更多的是“为什么”而不是“如何”。
  • 为什么要正确子类化 dict 类型,您需要执行以下操作:stackoverflow.com/questions/3387691/…。在透明度方面。我想这是一个品味问题。如果可以避免的话,我不想在整个项目中使用特殊的字典。
【解决方案2】:

我不知道编码器到底是做什么的,但它不是用 C 编写的,json 包的 Python 源代码在这里:http://hg.python.org/cpython/file/2a872126f4a1/Lib/json

另外,如果您只是想订购商品,还有

json.dumps(A(), sort_keys=True)

另请参阅this question ("How to perfectly override a dict?") 及其第一个答案,这说明您应该在大多数情况下继承 collections.MutableMapping。

或者只是给一个子类编码器,就像aychedee提到的那样。

【讨论】:

  • 这是正确答案!谢谢,我以前从未注意到转储的 sort_keys 选项。呸! :)
  • 是的,但是如果每个嵌入的字典都需要不同的顺序呢?
  • encoder.py 第 241 行:_iterencode = c_make_encoder(markers, self.default, _encoder, self.indent, self.key_separator, self.item_separator, self.sort_keys, self.skipkeys, self.allow_nan)
猜你喜欢
  • 2013-01-15
  • 2018-05-18
  • 1970-01-01
  • 2013-06-10
  • 2015-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多