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

时间:2018-07-03 13:40:33

标签: python json python-3.x dictionary ujson

所以我有一个字典,它是我从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,根据此benchmark,该模块速度更快,但我没有发现任何改进。

我尝试使用多线程:

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

pool.close()
pool.join()

我在chunksize上玩了一点,但是结果是一样的,仍然花费太多时间。

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

2 个答案:

答案 0 :(得分:4)

假设值确实是有效的JSON,则构建单个 JSON对象进行解码可能会更快。我认为将这些值连接到单个字符串中应该是安全的。

using UnityEngine;
using System.Collections;
using MySql.Data.MySqlClient;
using System;
using System.Linq;
using System.Collections.Generic;

public class MySqlTestScript : MonoBehaviour
{

private static MySqlTestScript _instnace;
public static MySqlTestScript sharedInstance()
{
    return _instnace;
}


string row = "";

public string host = "*****";
public string database = "*******";
public string usrename= "*******";
public string password = "******";

public List<Data> userData = new List<Data>();

 Data data;


void Awake()
{
    _instnace = this;
}


// Use this for initialization
public void Start()
{

    GetDataFromDatabase();
}

public string GetDataFromDatabase()
{
     string myConnectionString = "Server="+host+";Database="+database+";Uid="+usrename+ ";Pwd="+password+";";


    MySqlConnection connection = new MySqlConnection(myConnectionString);
    MySqlCommand command = connection.CreateCommand();
    command.CommandText = "SELECT * FROM Users";
    MySqlDataReader Reader;
    try
    {
        connection.Open();
        Reader = command.ExecuteReader();

        while (Reader.Read())
        {


            for (int i = 0; i < Reader.FieldCount; i++)
            {
                //rfid_tags.Add (Reader.GetString("UserName"));
                //rfid_tags.Add(Reader.GetString("RFID_Tag"));
                data = new Data(Reader.GetString("UserName"), Reader.GetString("RFID_Tag"));
                userData.Add(data);
                // ws.DomainUrl = reader.GetString("DomainUrl");
                // rfid_tags.Add(Reader.GetValue(i).ToString() + ",");
                // row += Reader.GetValue(i).ToString() + ", ";
            }

            Debug.Log(row);
        }
    }


    catch (Exception x)
    {
        Debug.Log(x.Message);
        return x.Message;
    }

    connection.Close();
    return row;

}

}

public class Data {

public string username { get; set; }
public string rfid { get; set; }

public Data(string _name, string _rfid)
{
    username = _name;
    rfid = _rfid;
}


public void SetUserName(string _userName) { username = _userName; }
public void SetRFID(string _rfid) { rfid = _rfid; }


}

用一次调用void OnTagsReported(ImpinjReader sender, TagReport report) { Debug.Log("OnTagsReported"); // This event handler is called asynchronously // when tag reports are available. // Loop through each tag in the report // and print the data. foreach (Tag tag in report) { Debug.Log(tag.Epc); // Debug.Log(MySqlTestScript.sharedInstance().rfid_tags[0]); Debug.Log("STEP ONE"); for (int i = 0; i < MySqlTestScript.sharedInstance().userData.Count; i++) { Debug.Log("STEP TWO"); if (tag.Epc.ToString().Trim() == MySqlTestScript.sharedInstance().userData[i].rfid) { Debug.Log("STEP THREE"); // TODO References the Name Loom.QueueOnMainThread(() => { namesTxt[i].text = MySqlTestScript.sharedInstance().userData[i].username; }); } } 和一次字符串格式化操作的较小开销,代替了调用>>> 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'}] 2000次以上的开销。

答案 1 :(得分:1)

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

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的{​​{1}}提示可以在两者之间进行权衡。

正确的性能分析应揭示延迟的来源。