В повседневных задачах есть множество инструментов для работы с различными форматами данных, такими как JSON, TOML, YAML и другими.

msgspec — это инструмент, который может работать со всеми этими форматами и при этом быть быстрым и простым в использовании. Для всех форматов один импорт, что в рамках работы с данной библиотекой является преимуществом. Если вам необходимо парсить много разных форматов данных, то эта библиотека точно подойдет вам.

Библиотека содержит:

  • Высокопроизводительные кодеры/декодеры для распространенных протоколов: JSON, MessagePack, YAML и TOMI.

  • Поддержка большого количества типов данных.

  • Быстрая валидация данных

  • Структуры, позволяющие представлять данные (похожие на attrs/pydantic, но работают быстрее)

Для начала установим библиотеку:

 pip3 install "msgspec[toml,yaml]"

Без дополнительных параметров библиотека установится с поддержкой json и messagepack.

Сериализация / десериализация

Для сериализации и десериализации данных нужен формат данных и входное значение.

import msgspec

a = {"1": 2, "3": 4}
encoded_json_data = msgspec.json.encode(a)
print(encoded_json_data)  # b'{"1": 2, "3": 4}'

decoded_data = msgspec.json.decode(encoded_json_data)
print(decoded_data)  # {'1': 2, '3': 4}

yaml_data = msgspec.yaml.encode(a)
print(yaml_data)  # b"'1': 2\n'3': 4\n"

Можно сделать вывод, что достаточно написать msgspec.{format}.[encode/decode], где format это json, yaml, toml, msgpack.

Валидация

Для валидации данных предлагается создавать классы, наследуясь от msgspec.Struct, аналогичные как в attrs/dataclsses/pydantic.

class User(msgspec.Struct):
    name: str
    surname: str
    email: str | None = None


print(msgspec.json.decode(b'{"name":"vasya","surname":"pupkin"}', type=User))
# User(name='vasya', surname='pupkin', email=None)

print(msgspec.json.decode(b'{"name":"vasya","surname":123}', type=User))
# print(msgspec.json.decode(b'{"name":"vasya","surname":123}', type=User))
#       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# msgspec.ValidationError: Expected `str`, got `int` - at `$.surname`

Добавляется только аргумент type , который определяет схему.

Бенчмарки

Сравним msgspec по скорости с самыми популярными библиотеками сериализации:

Для этого берем большие файлы и для каждого кейса прогоняем тысячу раз, сравниваем среднее время

Входные данные
tests = [
    # JSON
    {
        "msgspec.json.decode(data)": {"data": json_text},
        "json.loads(data)": {"data": json_text},
    },
    {
        "msgspec.json.encode(data)": {"data": json_data},
        "json.dumps(data)": {"data": json_data},
    },
    {
        "msgspec.json.decode(data)": {"data": json_text},
        "orjson.loads(data)": {"data": json_text},
    },
    {
        "msgspec.json.encode(data)": {"data": json_data},
        "orjson.dumps(data)": {"data": json_data},
    },
    # TOML
    {
        "msgspec.toml.decode(data)": {"data": toml_text},
        "toml.loads(data)": {"data": toml_text},
    },
    {
        "msgspec.toml.decode(data)": {"data": toml_text},
        "tomllib.loads(data)": {"data": toml_text},
    },
    {
        "msgspec.toml.encode(data)": {"data": toml_data},
        "toml.dumps(data)": {"data": toml_data},
    },
    # YAML
    {
        "msgspec.yaml.decode(data)": {"data": yaml_text},
        "yaml.load(data, Loader=yaml.Loader)": {
            "data": yaml_text,
        },
    },
    {
        "msgspec.yaml.decode(data)": {"data": yaml_text},
        "yaml.load(data, Loader=yaml.CLoader)": {
            "data": yaml_text,
        },
    },
    {
        "msgspec.yaml.encode(data)": {"data": yaml_data},
        "yaml.dump(data, Dumper=yaml.Dumper)": {
            "data": yaml_data,
        },
    },
    {
        "msgspec.yaml.encode(data)": {"data": yaml_data},
        "yaml.dump(data, Dumper=yaml.CDumper)": {
            "data": yaml_data,
        },
    },
]

Тест

Время (мс)

Msgspec (мс)

Ускорение

json.loads(data)

707.8

315.4

2.2

json.dumps(data)

946.4

118.6

8.0

orjson.loads(data)

306.2

315.7

1.0

orjson.dumps(data)

83.9

118.2

0.7

toml.loads(data)

1,017.9

420.0

2.4

tomllib.loads(data)

420.4

420.2

1.0

toml.dumps(data)

154.0

170.3

0.9

yaml.load(data, Loader=yaml.Loader)

16,142.7

1,602.5

10.1

yaml.load(data, Loader=yaml.CLoader)

1,625.4

1,603.1

1.0

yaml.dump(data, Dumper=yaml.Dumper)

10,032.0

1,595.4

6.3

yaml.dump(data, Dumper=yaml.CDumper)

1,600.5

1,594.8

1.0

Можно сделать вывод, что под капотом у библиотеки лучшие парсеры. Если вы пользуетесь только json, то стоит присмотреться к другой библиотеке (например, orjson), но все же значения библиотек не сильно критично разнятся. Также на примере yaml msgspec использует стандартные C имплементации.

Кроме того, есть бенчмарки от создателей библиотеки, которые сравнивают скорость сериализации и валидации данных.

Если в вашем коде требуется работа с различными форматами данных, например ваша программа принимает конфиг в yaml, поднимает API, которое отдаёт json и при этом не завязана на какой-нибудь pydantiс, то вам стоит попробовать msgspec. Также у библиотеки есть плюшки в виде json схем.

Комментарии (5)


  1. Ryav
    20.07.2024 15:40

    Почему нет сравнения с Pydantic v2?


    1. Andrey_Solomatin
      20.07.2024 15:40

      Может не хотят портить статистику?


      1. 9982th
        20.07.2024 15:40

        На официальном сайте есть сравнение по скорости

        По функциональности совпадение достаточно большое, но не стопроцентное.


    1. Dominux
      20.07.2024 15:40

      Pydantic не слабо рассчитан и на валидацию, так что сравнение не совсем честное


  1. Andrey_Solomatin
    20.07.2024 15:40

    С YAML бывают проблемы из-за неполной поддержки части стандартов. Последняя версия формата либо не поддерживается, либо не полно поддерживается. Какой стандарт используется в статье?