- Автор темы
- #1

DataClasses — встроенный инструмент Python, представленный в стандартной библиотеке начиная с версии 3.7. Он создан для упрощения и автоматизации работы с классами, которые в основном используются для хранения и обмена данными. А еще улучшает типизацию в более-менее серьезных проектах.
По своей сути это декоратор, который автоматически генерирует методы init, repr, eq и другие для вашего класса. Это избавляет разработчика от написания шаблонного кода и экономит гору времени.
Где применяется
- Структурирование данных: DataClasses идеально подходит для создания DTO (Data Transfer Objects) или моделей данных.
- Читаемость кода: упрощает понимание структуры данных за счет минималистичного синтаксиса.
- Интеграция с другими инструментами: отлично работает с ORM, API и прочими библиотеками.
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 содержит метаданные (например, имена полей). Это увеличивает объем используемой памяти.
- При сериализации в JSON или словарь создаются промежуточные структуры данных, также занимающие память.
- Сериализация и десериализация требуют множества операций, таких как рекурсивный обход объектов. Это увеличивает нагрузку на CPU.
- Использование встроенных функций Python (например, json.dumps() или DataClasseses.asdict()) добавляет накладные расходы.
Msgspec: скорость и минимализм

Источник
Msgspec создал разработчик Джим Крист-Хариф (Jim Crist-Harif). Это библиотека для сериализации и десериализации данных с акцентом на производительность. Поддерживает форматы JSON, MessagePack и другие. Msgspec позволяет создавать типизированные структуры данных, похожие на DataClasses, но с фокусом на скорости.
Где применяется
- Высоконагруженные системы: 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 минимизируют количество операций, необходимых для сериализации и десериализации.
- Библиотека написана на C (или использует C-расширения) и работает быстро.
Что в итоге
Вот сравнительная таблица производительности между 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. Он быстрее, легковеснее и лучше подходит для задач, где каждая миллисекунда на счету.
В конечном счете оба инструмента достойны внимания, их можно использовать вместе, если того требует проект. Главное — понимать, какой модуль лучше подходит для конкретной задачи.