Самый быстрый питонический способ парсинга словаря, где значения - байты строкового объекта json
Итак, у меня есть словарь, который является хеш-объектом, который я получаю от 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()]
Это работает, но для большого набора данных, это занимает слишком много времени.
Я переключаюсь на использование стороннего json-модуля ujson, который работает быстрее в соответствии с этим тестом, но я не заметил каких-либо улучшений.
Я попытался использовать многопоточность:
pool = Pool()
final_data = pool.map(ujson.loads, source_data.values(), chunksize=500)
pool.close()
pool.join()
Я немного поиграл с chunksize
но результат тот же, все еще слишком много времени.
Было бы очень полезно, если бы кто-то мог предложить другое решение или усовершенствование предыдущих попыток, было бы идеально, если бы я мог избежать использования цикла.
2 ответа
Предполагая, что значения действительно являются допустимыми 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'}]
Это заменяет накладные расходы на звонки json.loads
2000+ раз с меньшими накладными расходами одного звонка b','.join
и единственная операция форматирования строки.
Для справки я попытался воспроизвести ситуацию:
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
,
Надлежащее профилирование должно показать, откуда происходят задержки.