解析字典的最快pythonic方法,其中值是字节字符串化的json对象

Posted

技术标签:

【中文标题】解析字典的最快pythonic方法,其中值是字节字符串化的json对象【英文标题】:Fastest pythonic way of parsing dictionary where values are bytes stringfied json object 【发布时间】:2018-12-11 20:34:52 【问题描述】:

所以我有一个字典,它是我从 Redis 获取的哈希对象,类似于以下字典:

source_data = 
   b'key-1': b'"age":33,"gender":"Male"', 
   b'key-2': b'"age":20,"gender":"Female"'

我的目标是从该字典中提取所有值,并将它们作为 Python 字典列表,如下所示:

final_data = [
   
      'age': 33,
      'gender': 'Male'
   ,

   
      'age': 20,
      'gender': 'Female'
   
]

我尝试使用 json 解析进行列表理解:

import json
final_data = [json.loads(a) for a in source_data.values()]

它可以工作,但对于大型数据集,它需要太多时间。

我切换到使用这个 3rd 方 json 模块 ujson 根据这个 benchmark 更快,但我没有注意到任何改进。

我尝试使用多线程:

pool = Pool()
final_data = pool.map(ujson.loads, source_data.values(), chunksize=500)

pool.close()
pool.join()

我玩了一点chunksize,结果还是一样,还是太费时间了。

如果有人可以建议其他解决方案或改进以前的尝试,那将非常有帮助,如果我可以避免使用循环,那将是理想的。

【问题讨论】:

也许值得尝试 pypy? 需要多长时间,您的数据源有多大? @TomDalton 目前无法尝试 pypy。 多处理在这里更有可能阻碍而不是帮助。你想反序列化一个字符串,但是一旦子进程完成了他们必须将对象序列化为字符串,将字符串发送到父进程,然后再将字符串反序列化为对象......唯一的区别是多处理不使用 JSON 作为其数据交换格式。 如果需要 35 秒,我高度怀疑这里还有其他一些我们忽略的瓶颈。 json.loads 应该能够在微秒内处理这样的小数据。你是在一个批次中从 redis 中获取所有数据吗?或者您是按顺序从远程 redis 服务器请求条目? (只是推测,但这种延迟对于网络 i/o 来说更为典型) 【参考方案1】:

假设这些值确实是有效的 JSON,那么构建一个 single JSON 对象进行解码可能会更快。我认为将值加入单个字符串应该是安全的。

>>> new_json = b'[%s]' % (b','.join(source_data.values(),)
>>> new_json
b'["age":33,"gender":"Male","age":20,"gender":"Female"]'
>>> json.loads(new_json)
['age': 33, 'gender': 'Male', 'age': 20, 'gender': 'Female']

这将调用json.loads 2000+ 次的开销替换为对b','.join 的单个调用和单个字符串格式化操作的较小开销。

【讨论】:

我已经试过了,但是你看到的值是字节字符串。 抱歉,懒得在 Python 2 中测试了。更新应该在 Python 3 中工作。 感谢您的尝试,它可以工作,但解析 2000 多个条目仍需要 34 多秒。 如果仅仅解码几千个条目需要几秒钟的时间,这里肯定有其他事情发生。您能否发布示例数据的链接,以便我们自己复制? minimal reproducible example 这突然似乎非常快,大约 4000 个条目,在 0.12 秒内解析它们【参考方案2】:

作为参考,我尝试复制这种情况:

import json, timeit, random
source_data =  'key-'.format(n).encode('ascii'): 
                '"age":,"gender":""'.format(
                    random.randint(18,75), 
                    random.choice(("Male", "Female"))
                 ).encode('ascii') 
               for n in range(45000) 
timeit.timeit(" k: json.loads(v) for (k,v) in source_data.items() ", 
    number=1, globals='json': json, 'source_data': source_data)

这在不到一秒钟的时间内完成。那些超过 30 秒的时间一定来自我没有看到的东西。

我最接近的猜测是您将数据放在某种代理容器中,其中每个键提取都变成了远程调用,例如使用hscan 而不是hgetall。使用count 提示hscan 应该可以在两者之间进行权衡。

正确的分析应该可以揭示延迟的来源。

【讨论】:

以上是关于解析字典的最快pythonic方法,其中值是字节字符串化的json对象的主要内容,如果未能解决你的问题,请参考以下文章

读取 .csv 文件时在 Python 中解析日期的最快方法是啥?

从字典创建 Python DataFrame,其中键是列名,值是行

将字典的键和值从“unicode”转换为“str”的最快方法?

js从字典里取到一个字,用啥算法最快

在 Python 中保存和加载大型字典的最快方法

在 Python 中从字节字符串中删除前 20 个字节的最快方法是啥?