• Добро пожаловать на сайт - Forumteam.bet !

    Что бы просматривать темы форума необходимо зарегестрироваться или войти в свой аккаунт.

    Группа в телеграме (подпишитесь, что бы не потерять нас) - ForumTeam Chat [Подписатся]
    Связь с администратором - @ftmadmin

Msgspec vs DataClasses: битва инструментов в мире Python-сериализации

Article Publisher

Публикатор
Команда форума
Регистрация
05.02.25
1739805497099.png


DataClasses — встроенный инструмент Python, представленный в стандартной библиотеке начиная с версии 3.7. Он создан для упрощения и автоматизации работы с классами, которые в основном используются для хранения и обмена данными. А еще улучшает типизацию в более-менее серьезных проектах.

По своей сути это декоратор, который автоматически генерирует методы init, repr, eq и другие для вашего класса. Это избавляет разработчика от написания шаблонного кода и экономит гору времени.

Где применяется​

  • Структурирование данных: DataClasses идеально подходит для создания DTO (Data Transfer Objects) или моделей данных.
  • Читаемость кода: упрощает понимание структуры данных за счет минималистичного синтаксиса.
  • Интеграция с другими инструментами: отлично работает с ORM, API и прочими библиотеками.
А теперь — пример использования DataClasses. Допустим, нам нужно создать DTO для пользователя:

from dataclasses import dataclass

@dataclass
class User:
first_name: str
last_name: str
email: str
age: int
Попробуем создать объект:

>>> User("Alyosha", "Popovich", "[email protected]", "16")
User(first_name='Alyosha', last_name='Popovich', email='[email protected]', age='16')
Получилось, но возраст должен быть int, а не str. И ошибки валидации мы не увидели. Исправим это при помощи метода __post_init__.

from dataclasses import dataclass

@dataclass
class User:
first_name: str
last_name: str
email: str
age: int

def __post_init__(self):
if not isinstance(self.age, int):
raise Exception("Age must be an integer")
Попробуем еще раз:

>>> User("Alyosha", "Popovich", "[email protected]", "16")
Traceback (most recent call last):
File "<python-input-13>", line 1, in <module>
User("Alyosha", "Popovich", "[email protected]", "16")
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<string>", line 7, in __init__
File "<python-input-11>", line 10, in __post_init__
raise Exception("Age must be an integer")
Exception: Age must be an integer
Ура! Теперь у нас работает валидация. А еще мы можем «заморозить» наш класс при помощи frozen=True:

from dataclasses import dataclass
@dataclass(frozen=True)
class User:
first_name: str
last_name: str
email: str
age: int

def __post_init__(self):
if not isinstance(self.age, int):
raise Exception("Age must be an integer")
Тогда нельзя будет изменить атрибуты экземпляра класса после его инициализации

>>> user_1 = User("Alyosha", "Popovich", "[email protected]", 16)
>>> user_1.first_name
'Alyosha'
>>> user_1.first_name = "Иван"
Traceback (most recent call last):
File "<python-input-13>", line 1, in <module>
user_1.first_name = "Иван"
^^^^^^^^^^^^^^^^^
File "<string>", line 19, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'first_name'

Производительность​

Скорость сериализации и десериализации. DataClasses не предназначен для высокоскоростной сериализации. Он использует стандартные механизмы Python для преобразования объектов в словари или JSON. Это удобно, но не быстро.

  • Сериализация: преобразование объектов dataclasses в JSON или словарь выполняется встроенными методами Python, например dataclasses.asdict(). Но метод рекурсивно копирует данные — а это может привести к увеличенному потреблению памяти и снижению производительности на больших структурах.
  • Десериализация: обратный процесс (из JSON или словаря в объект) тоже требует дополнительных шагов: создание экземпляра класса и заполнение его полей.
Использование памяти. DataClasses создает объекты, которые хранят данные в виде атрибутов. Это удобно, но не всегда эффективно с точки зрения памяти.

  • Каждый объект DataClasses содержит метаданные (например, имена полей). Это увеличивает объем используемой памяти.
  • При сериализации в JSON или словарь создаются промежуточные структуры данных, также занимающие память.
Нагрузка на CPU. DataClasses полагается на стандартные механизмы Python, они не всегда оптимизированы для производительности.

  • Сериализация и десериализация требуют множества операций, таких как рекурсивный обход объектов. Это увеличивает нагрузку на CPU.
  • Использование встроенных функций Python (например, json.dumps() или DataClasseses.asdict()) добавляет накладные расходы.
DataClasses не оптимизирован для высокой производительности при сериализациии и десериализации. Он создан для удобства, а не скорости.

Msgspec: скорость и минимализм​

Источник

Источник
Msgspec создал разработчик Джим Крист-Хариф (Jim Crist-Harif). Это библиотека для сериализации и десериализации данных с акцентом на производительность. Поддерживает форматы JSON, MessagePack и другие. Msgspec позволяет создавать типизированные структуры данных, похожие на DataClasses, но с фокусом на скорости.

Где применяется​

  • Высоконагруженные системы: Msgspec идеально подходит для микросервисов, где важна скорость обработки данных.
  • Сериализация больших объемов данных: если вам нужно быстро упаковать или распаковать данные, Msgspec справится лучше многих аналогов.
  • Минималистичные проекты: Msgspec легковесен и не требует сложных зависимостей.
Повторим пример выше, только теперь для Msgspec:

import msgspec

class User(msgspec.Struct):
first_name: str
last_name: str
email: str
age: int
Инициализируем объект и указываем вместо целого числа для параметра age — строку.

>>> User("Alyosha", "Popovich", "[email protected]", "16")
User(first_name='Alyosha', last_name='Popovich', email='[email protected]', age='16')
Объект создается и ошибки не возникает.

Но у Msgspec есть одна особенность — типы данных проверяются в момент декодирования. Например, вот что получим, если выполним декодирование:

>>> msgspec.json.decode(b'{"first_name": "Alyosha", "last_name": "Popovich", "email": "[email protected]", "age": "16"}', type=User)
Traceback (most recent call last):
File "<python-input-8>", line 1, in <module>
msgspec.json.decode(b'{"first_name": "Alyosha", "last_name": "Popovich", "email": "[email protected]", "age": "16"}', type=User)
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
msgspec.ValidationError: Expected `int`, got `str` - at `$.age`
У нас ошибка валидации на месте age.

Как можно заметить, при самом процессе инициализации никакой валидации нет. Тут тоже можно воспользоваться методом __post_init__.

import msgspec

class User(msgspec.Struct):
first_name: str
last_name: str
email: str
age: int

def __post_init__(self):
if not isinstance(self.age, int):
raise Exception("Age must be an integer")
При создании объекта с некорректным типом данных у поля age получаем ошибку:

>>> User("Alyosha", "Popovich", "[email protected]", "16")
Traceback (most recent call last):
File "<python-input-3>", line 1, in <module>
User("Alyosha", "Popovich", "[email protected]", "16")
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<python-input-2>", line 9, in __post_init__
raise Exception("Age must be an integer")
Exception: Age must be an integer.
Скорость сериализации и десериализации. Msgspec разработан с акцентом на производительность. Он использует оптимизированные алгоритмы для сериализации и десериализации данных.

  • Сериализация: Msgspec напрямую преобразует объекты в бинарные форматы (например, MessagePack) или JSON, минимизируя использование памяти и CPU.
  • Десериализация: процесс обратного преобразования оптимизирован, так как Msgspec применяет низкоуровневые операции для обработки данных.
Использование памяти. Msgspec оптимизирован для работы с памятью. Он использует компактные структуры данных и минимизирует накладные расходы.

  • Объекты Msgspec хранят данные в более компактном формате, а это снижает использование памяти.
  • При сериализации Msgspec избегает создания промежуточных структур, что экономит память.
Нагрузка на CPU. Msgspec использует низкоуровневые оптимизации, чтобы снизить нагрузку на CPU:

  • Алгоритмы Msgspec минимизируют количество операций, необходимых для сериализации и десериализации.
  • Библиотека написана на C (или использует C-расширения) и работает быстро.
Msgspec гораздо быстрее DataClasses — за счет оптимизированного низкоуровневого кода и минималистичного подхода.

Что в итоге​

Вот сравнительная таблица производительности между msgspec.Struct и стандартными Python dataclass. Данные взяты из официальных бенчмарков msgspec и дополнительных тестов:

Операция
msgspec.Struct
dataclass
Разница
Импорт
класса (мкс)
12.51​
506.09​
~40x​
Создание
экземпляра (мкс)
0.09​
0.36​
~4x​
Сравнение
на равенство (мкс)
0.02​
0.14​
~7x​
Сравнение
порядка (мкс)
0.03​
0.16​
~5x​
Мкс — микросекунды

Результаты показывают, что msgspec.Struct превосходит стандартные dataclass по всем измеренным параметрам. Так что выбор между ними зависит от особенностей вашего проекта и личных предпочтений.

Если нужны удобство и читаемость, вы работаете с небольшими объемами данных, то DataClasses — отличный выбор. Модуль прост в использовании и интегрируется практически с любыми инструментами Python.

Если же цель — максимальная производительность и вы работаете с большими объемами данных или высоконагруженными системами, выбирайте Msgspec. Он быстрее, легковеснее и лучше подходит для задач, где каждая миллисекунда на счету.

В конечном счете оба инструмента достойны внимания, их можно использовать вместе, если того требует проект. Главное — понимать, какой модуль лучше подходит для конкретной задачи.
 
Сверху Снизу