解析字典的最快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,其中键是列名,值是行