首页 > 解决方案 > `ast.literal_eval` 的最快实现

问题描述

str我有一些bytes可以通过ast.literal_eval.

(它由一个dicts列表组成,其中dict键是字符串,值是字符串,int或float。但也许这个问题对于任何可以通过解析的字符串都是通用的ast.literal_eval。)

它很大:~22MB 未压缩。

解析它的最快方法是什么?

当然我可以使用ast.literal_eval,但这似乎很慢。标准eval稍快(有趣的是,但可能与预期的一样,取决于您对 Python 的了解程度;请参阅 的实现ast.literal_eval)但仍然很慢。

相比之下,当我将相同的数据序列化为 JSON,然后加载 JSON ( )json.loads时,这要快得多 (>10x)。所以这表明原则上应该可以同样快地解析它。

一些统计数据:

Gunzip + read time: 0.15111494064331055
Size: 22035943
compile: 3.1023156170000004
parse: 3.3381092380000004
eval: 3.0252232049999996
ast.literal_eval: 3.765798232
json.loads: 0.2657175249999994

可以找到此基准脚本以及生成此类虚拟文本文件的脚本:here

(也许答案是:“这需要更快的 C 实现;还没有人实现过”)


好的,发布后,我发现了一些相关问题。虽然我没有通过谷歌找到它们(也许我的搜索词“faster literal_eval”很糟糕)。

这部分回答了这个问题。

标签: python

解决方案


因此,据我所知,目前不存在比ast.literal_eval(嗯,eval它本身更快,但不安全)的实现。

所以我实现了我自己的简单实现,它将文字 Python 代码转换为等效的二进制 Pickle 数据。因此,对于某些字节,您将使用,data而不是,并获得 5.5 倍的加速。ast.literal_eval(data.decode("utf8"))pickle.loads(py_to_pickle(data))

回购在这里。这是 C++ 中一个非常直接的实现,您可以轻松地直接使用它ctypes(repo 中有一个示例)。

新统计:

Gunzip + read time: 0.1663219928741455
Size: 22540270
py_to_pickle: 0.539439306
pickle.loads+py_to_pickle: 0.7234611099999999
compile: 3.3440755870000003
parse: 3.6302585899999995
eval: 3.306765757000001
ast.literal_eval: 4.056752016000003
json.loads: 0.3230752619999997
pickle.loads: 0.1351051709999993
marshal.loads: 0.10351717500000035

推荐阅读