Самый быстрый питонический способ парсинга словаря, где значения - байты строкового объекта json - PullRequest
0 голосов
/ 03 июля 2018

Итак, у меня есть словарь, который является хеш-объектом, который я получаю от 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 ]

0 голосов
/ 03 июля 2018

Для справки я попытался воспроизвести ситуацию:

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.

Надлежащее профилирование должно показать, откуда происходят задержки.

0 голосов
/ 03 июля 2018

Если предположить, что значения действительно являются допустимыми 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 и одну операцию форматирования строки.

...