Python:自定义JSON解码器的性能

时间:2012-09-13 17:08:49

标签: python json python-3.x

我有一个应用程序,它使用标准的JSON工具定期将JSON文件转储并加载到Python中。

早期,我们认为使用加载的JSON数据作为对象而不是字典更方便。这实际上归结为“点”成员访问的便利性,而不是字典键查找的[]符号。 Javascript的一个优点是字典查找和成员数据访问之间没有真正的区别(这就是为什么JSON特别适合Javascript,我猜)。但在Python中,字典键和对象数据成员是不同的东西。

因此,我们的解决方案是使用自定义JSON解码器,它使用object_hook函数返回对象而不是字典。

我们过着幸福的生活......直到现在,这个设计决定可能会成为一个错误。你看,现在JSON转储文件已经变得相当大(> 400 MB)。据我所知,标准的Python 3 JSON工具使用本机代码进行实际的解析,因此它们非常快。但是如果你提供一个自定义object_hook,它仍然必须为解码的每个JSON对象执行解释的字节代码 - 这会严重降低速度。没有object_hook,解码整个400 MB文件只需要大约20秒。但有了钩子,它需要半个多小时!

所以,在这一点上,我想到了两个选项,两者都不是很令人愉快。一种是忘记使用“点”成员数据访问的便利性,只使用Python词典。 (这意味着更改大量代码。)另一种是编写C扩展模块并将其用作object_hook,看看我们是否获得了任何加速。

我想知道是否有一些我没想到的更好的解决方案 - 也许是一种更简单的方法来获得“点”成员访问,同时最初解码为Python字典。

对此问题的任何建议,解决方案?

3 个答案:

答案 0 :(得分:3)

你可以尝试代替使用object_hook,让json返回一个字典,然后将该字典转储到一个namedtuple中。

这样的事情:

from collections import namedtuple
result = json.parse(data)
JsonData = namedtuple("JsonData", result.keys())
jsondata = JsonData(**result)

我不知道它的速度是多少,但值得一试。

答案 1 :(得分:0)

使用返回的本机JSON模块的dict并用一个提供点访问的对象包装它怎么样?

您可以执行以下操作:

class DictWrap(object):

def __init__(self, d):
    self.__d = d

def __getattr__(self, attr):
    try:
        return self.__d[attr]
    except KeyError:
        raise AttributeError


dw = DictWrap({"a": "foo", "b": "bar"})

print dw.a, dw.b // foo bar
print dw.c // AttributeError

编辑:刚刚看到Lennart Regebro的回答,我会去那里。

答案 2 :(得分:0)

我取决于使用情况。

Lennart Regebro解决方案可以完美地用于普通字典案例(对于您的案例可能不适用)。否则,您需要实现递归解决方案。 但在这种情况下 - python将为json中的每个字典创建一个类。

来自nemo的解决方案更加“懒惰”/“按需”,所以如果你不打算使用字典的每个字段,我会选择nemo的解决方案。但是为嵌套的字典和数组修改它。

def __getattr__(self, attr):
  ...
  if isinstance(self.__d[attr], dict):
    return DictWrap(self.__d[attr])

  elif isinstance(self.__d[attr], list):
    return ListWrap(self.__d[attr])    # and create similar wrapper for List.
  ...

普通词典的另一个解决方案是:

class JsonData(object):pass

data = JsonData()
data.__dict__.update(json.parse(data))