美文网首页
一个更快、支持对更多数据类型进行序列化、反序列化的Python库

一个更快、支持对更多数据类型进行序列化、反序列化的Python库

作者: 北国的秋天 | 来源:发表于2021-11-17 20:10 被阅读0次

    一个快速正确的Python JSON库,它本身就支持数据类、日期、时间、numpy 数据类型。

    最近,在开发中遇到需要对大量复杂JSON进行序列化和反序列化操作场景,但发现内置的json库速度非常慢,想使用第三方库替代它,于是发现了上面提到的 orjson 库。

    下面,让我们看一下orjson 与其他Python json库相比的优缺点。

    序列化数据类(dataclasses.dataclas)实例的速度是其他库的40-50倍。

    将datetime、date和time实例序列化为RFC 3339格式,例如,“1970-01-01T00:00:00+00:00”。

    序列化 numpy.ndarray的实例化速度是其他库的0.3倍。

    序列化为字节而不是字符串,也就是说,不是临时替换。

    序列化字符串而不将unicode转义为ASCII,例如,"好"而不是"\u597d"。

    序列化浮点型数据的速度是其他库的10倍,反序列化的速度是其他库的两倍。

    具有严格的UTF-8和JSON格式一致性,比标准库更正确。

    不额外提供用于读取/写入类文件对象的load()或dump()函数。

    安装

    使用pip 直接安装,如下:

    pip install orjson

    简单示例

    下面是一个含序列化、反序列化的示例:

    data = {"emoji": "", "integer": 9527, "float": 9.527, "boolean": False,

            "list": ["", 9527, 9.527, False],

            "dict": {"key1": "value1", "key2": "value2"},

            "chinese": "您好", "japanese": "こんにちは",

            "created_at": datetime.datetime(1970, 1, 1),

            "status": "🆗", "payload": numpy.array([[1, 2], [3, 4]])}

    # 序列化 data

    data_dumps = orjson.dumps(data, option=orjson.OPT_NAIVE_UTC | orjson.OPT_SERIALIZE_NUMPY)

    print(data_dumps)

    # 反序列化 data_dumps

    data_loads = orjson.loads(data_dumps)

    print(data_loads)

    # 执行上述代码,输出结果为:

    b'{"emoji":"\xf0\x9f\x98\x82","integer":9527,"float":9.527,"boolean":false,"list":["\xf0\x9f\x98\x82",9527,9.527,false],"dict":{"key1":"value1","key2":"value2"},"chinese":"\xe6\x82\xa8\xe5\xa5\xbd","japanese":"\xe3\x81\x93\xe3\x82\x93\xe3\x81\xab\xe3\x81\xa1\xe3\x81\xaf","created_at":"1970-01-01T00:00:00+00:00","status":"\xc3\xb0\xc5\xb8\xe2\x80\xa0\xe2\x80\x94","payload":[[1,2],[3,4]]}'

    {'emoji': '', 'integer': 9527, 'float': 9.527, 'boolean': False, 'list': ['', 9527, 9.527, False], 'dict': {'key1': 'value1', 'key2': 'value2'}, 'chinese': '您好', 'japanese': 'こんにちは', 'created_at': '1970-01-01T00:00:00+00:00', 'status': '🆗', 'payload': [[1, 2], [3, 4]]}

    因头条显示问题,部分数据显示不出,特附上述代码截图,如下:

    序列化

    参数说明

    对于序列化,可以指定以下两个输入参数:

    *default:要序列化子类或任意类型,可指定default作为返回受支持类型的可调用对象。 此外,您可以通过引发诸如TypeError之类的异常来强制执行规则以处理不受支持的日期类型。

    option:若要修改数据序列化的方式,请指定选项。在orjson 中,每个选项都是一个整数常量。要指定多个选项,将它们一起屏蔽,例如: option=orjson.OPT_STRICT_INTEGER | orjson.OPT_NAIVE_UTC 。

    序列化 default 参数

    当输入包含不支持的Decimal数据类型时,则会引发错误。

    import decimal

    orjson.dumps(decimal.Decimal("3.141592653"))

    运行上述代码,将得到以下输出:

    TypeError: Type is not JSON serializable: decimal.Decimal

    为了使得 orjson 序列化支持Decimal数据类型,我们可以创建一个 callable 函数或lambda 表达式并将其作为default参数传递,如下。

    def default(obj):

        if isinstance(obj, decimal.Decimal):

            return str(obj)

        raise TypeError

    data = orjson.dumps(decimal.Decimal("0.0842389659712649442845"), default=default)

    # 执行上述代码,输出结果为:

    b'"0.0842389659712649442845"'

    序列化 option 参数

    OPT_APPEND_NEWLINE :在输出中附加\n 。

    OPT_INDENT_2 :缩进两个空格的打印输出。

    OPT_NAIVE_UTC :将没有tzinfo的 datetime.datetime对象序列化为UTC。 这对设置了tzinfo datetime.datetime对象没有影响。

    OPT_NON_STR_KEYS :序列化字符串以外类型的字典键。它允许dict的键为字符串、整型、浮点型、布尔型、None、时间(datetime.time、datetime.datetime)、日期(datetime.date)、枚举(enum.Enum)、uuid.UUID。

    OPT_OMIT_MICROSECONDS :不要序列化datetime.datetime或datetime.time实例上的微秒级数据。

    OPT_PASSTHROUGH_DATACLASS: 支持序列化数据类(dataclasses.dataclas)实例时,通过default参数 定制化输出内容。

    OPT_PASSTHROUGH_DATETIME: 序列化datetime.datetime, datetime.date, and datetime.time实例时,通过default参数自定义格式。

    OPT_SERIALIZE_NUMPY :序列化numpy.ndarray实例。

    OPT_SORT_KEYS :按排序顺序对字典键进行序列化。 默认值是未指定顺序进行序列化。

    OPT_STRICT_INTEGER :对整数(而不是标准的64位)强制执行53位限制。

    代码示例,如下:

    import orjson, datetime, uuid

    # 序列化 dict键为uuid.UUID的数据

    orjson.dumps(

            {uuid.UUID("9527d115-6ff8-9aj1-n3b1-128sj384392135reiop"): [1, 2]},

            option=orjson.OPT_NON_STR_KEYS,

        )

    # 序列化 dict键为datetime.datetime的数据

    orjson.dumps(

            {datetime.datetime(2021, 1, 1, 0, 0, 0): [1, 2]},

            option=orjson.OPT_NON_STR_KEYS | orjson.OPT_NAIVE_UTC,

        )

    # 不序列化微妙字段

    orjson.dumps(

            datetime.datetime(2021, 1, 1, 0, 0, 0, 1),

            option=orjson.OPT_OMIT_MICROSECONDS,

        )

    # 序列化 dataclasses 数据时,通过default参数定制化输出内容

    import dataclasses 数据

    @dataclasses.dataclass

    class User:

        id: str

        name: str

        password: str

    def default(obj):

        if isinstance(obj, User):

            return { "name": obj.name,"password":obj.password}

        raise TypeError

    orjson.dumps(

            User("123", "Tom", "123456"),

            option=orjson.OPT_PASSTHROUGH_DATACLASS,

            default=default,

        )

    #  序列化datetime.datetime实例时,通过default参数自定义格式。

    def default(obj):

        if isinstance(obj, datetime.datetime):

            return obj.strftime("%a, %d %b %Y %H:%M:%S GMT")

        raise TypeError

      orjson.dumps(

            {"created_at": datetime.datetime(2021, 1, 1)},

            option=orjson.OPT_PASSTHROUGH_DATETIME,

            default=default,

        )

    反序列化

    loads() 接受bytes、bytearray、memoryview、STR输入。它反序列化为dict、list、int、float、str、bool和None对象。如果输入作为memoryview、bytearray或bytes对象存在,建议直接传递这些,而不是创建一个不必要的str对象。这样可以降低内存使用量和延迟,输入必须是有效的UTF-8。

    文件读/写

    通常,我们可以通过write() 函数将序列化后返回的字节数据保存到文件中。 但是,需要在mode参数中包含b模式 。

    data = {"emoji": "", "integer": 9527, "float": 9.527, "boolean": False,

            "list": ["", 9527, 9.527, False],

            "dict": {"key1": "value1", "key2": "value2"},

            "chinese": "您好", "japanese": "こんにちは",

            "created_at": datetime.datetime(1970, 1, 1),

            "status": "🆗", "payload": numpy.array([[1, 2], [3, 4]])

            }

    with open("example.json", "wb") as f:

        f.write(orjson.dumps(data, option=orjson.OPT_NAIVE_UTC | orjson.OPT_SERIALIZE_NUMPY))

    因头条显示问题,部分数据显示不出,特附上述代码截图,如下:

    生成的example.json 内容如下:

    同样,从文件中读取数据也很简单,如下所示:

    with open("example.json", "rb") as f:

        json_data = orjson.loads(f.read())

    print(json_data)

    # 执行上述代码,输出结果为

    {'emoji': '', 'integer': 9527, 'float': 9.527, 'boolean': False, 'list': ['', 9527, 9.527, False], 'dict': {'key1': 'value1', 'key2': 'value2'}, 'chinese': '您好', 'japanese': 'こんにちは', 'created_at': '1970-01-01T00:00:00+00:00', 'status': '🆗', 'payload': [[1, 2], [3, 4]]}

    最后,性能测试

    我们通过简单的测试,来比较一下json、ujson、orjson三者的序列化性能,其中json库为Pthon内置库,ujson 库是用 C实现的,orjson 库是用Rust 实现的。

    # -*- coding: utf-8 -*-

    import json

    import random

    import ujson

    import orjson

    import time

    def cost_time(func):

        def inner(*args, **kwargs):

            start_time = time.time()

            result = func(*args, **kwargs)

            stop_time = time.time()

            print("{0} 耗时:{1}".format(func.__name__, stop_time - start_time))

            return result

        return inner

    @cost_time

    def json_dumps(obj):

        return json.dumps(obj)

    @cost_time

    def ujson_dumps(obj):

        return ujson.dumps(obj)

    @cost_time

    def orjson_dumps(obj):

        return orjson.dumps(obj)

    if __name__ == '__main__':

        test = {}

        for i in range(1, 2000000):

            test[str(i)] = ''.join(random.sample(

                ['z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm',

                  'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e','d', 'c', 'b', 'a'], 10))

        json_dumps(test)

        ujson_dumps(test)

        orjson_dumps(test)

    我们可以看到,同样是序列含200万k-v 的dict对象,使用orjson的处理性能远比其他两个库效率高的多。

    json_dumps 耗时:1.1578669548034668

    ujson_dumps 耗时:0.45979905128479004

    orjson_dumps 耗时:0.09074163436889648

    看了这篇内容后,坚信以下两件事,也会对你的自我提升有一定的帮助:

    1、点赞,让更多人能看到,同时你的认可也会鼓励我创作更多优质内容。

    2、要让自己变得更强:想想,假如你是要在测试这个行业长期做下去,你的工作经验和测试技术是绝对不够的,你需要提升,你需要丰富你的技术栈!还等什么!

    这一些资料,对做【软件测试】的朋友而言应该是较为完整了,这类学习资料也陪伴我走过了最艰难的路程,希望也可以帮助到你!万事要尽早,尤其是技术行业,一定要提升技术功底。

    相关文章

      网友评论

          本文标题:一个更快、支持对更多数据类型进行序列化、反序列化的Python库

          本文链接:https://www.haomeiwen.com/subject/czdjtrtx.html