解析字典的最快 pythonic 方式,其中值是字节字符串化 json 对象
Fastest pythonic way of parsing dictionary where values are bytes stringfied json object
所以我有一个字典,它是我从 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()]
它有效,但对于大型数据集,它需要太多时间。
我改用这个第 3 方 json 模块 ujson which is faster according to this benchmark,但我没有发现任何改进。
我试过使用多线程:
pool = Pool()
final_data = pool.map(ujson.loads, source_data.values(), chunksize=500)
pool.close()
pool.join()
我用chunksize
玩了一下,结果还是一样,还是花了不少时间。
如果有人能提出另一个解决方案或改进以前的尝试,那将非常有帮助,如果我能避免使用循环,那将是理想的。
假设这些值确实有效 JSON,构建一个 单个 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'}]
这用单次调用 b','.join
和单次字符串格式化操作的较少开销取代了调用 json.loads
2000 多次的开销。
作为参考,我尝试复制这种情况:
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
rather than hgetall
。使用 count
提示 hscan
.
应该可以在两者之间进行权衡
正确的分析应该揭示延迟的来源。
所以我有一个字典,它是我从 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()]
它有效,但对于大型数据集,它需要太多时间。
我改用这个第 3 方 json 模块 ujson which is faster according to this benchmark,但我没有发现任何改进。
我试过使用多线程:
pool = Pool()
final_data = pool.map(ujson.loads, source_data.values(), chunksize=500)
pool.close()
pool.join()
我用chunksize
玩了一下,结果还是一样,还是花了不少时间。
如果有人能提出另一个解决方案或改进以前的尝试,那将非常有帮助,如果我能避免使用循环,那将是理想的。
假设这些值确实有效 JSON,构建一个 单个 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'}]
这用单次调用 b','.join
和单次字符串格式化操作的较少开销取代了调用 json.loads
2000 多次的开销。
作为参考,我尝试复制这种情况:
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
rather than hgetall
。使用 count
提示 hscan
.
正确的分析应该揭示延迟的来源。