惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
N
News and Events Feed by Topic
S
Schneier on Security
G
GRAHAM CLULEY
Scott Helme
Scott Helme
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
GbyAI
GbyAI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
C
CERT Recently Published Vulnerability Notes
T
The Exploit Database - CXSecurity.com
C
Cisco Blogs
T
The Blog of Author Tim Ferriss
Cisco Talos Blog
Cisco Talos Blog
P
Privacy & Cybersecurity Law Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 司徒正美
Blog — PlanetScale
Blog — PlanetScale
Project Zero
Project Zero
MyScale Blog
MyScale Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件
The Last Watchdog
The Last Watchdog
Vercel News
Vercel News
The Cloudflare Blog
C
Check Point Blog
Help Net Security
Help Net Security
Microsoft Security Blog
Microsoft Security Blog
AI
AI
Simon Willison's Weblog
Simon Willison's Weblog
云风的 BLOG
云风的 BLOG
M
MIT News - Artificial intelligence
Stack Overflow Blog
Stack Overflow Blog
腾讯CDC
NISL@THU
NISL@THU
S
Security @ Cisco Blogs
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
S
SegmentFault 最新的问题
MongoDB | Blog
MongoDB | Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threatpost
AWS News Blog
AWS News Blog
Cloudbric
Cloudbric
N
News and Events Feed by Topic
PCI Perspectives
PCI Perspectives
S
Securelist
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
Vulnerabilities – Threatpost
S
Secure Thoughts

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
3 ошибки при работе с dataclasses в Python
badcasedaily · 2026-05-19 · via Все публикации подряд на Хабре

Уровень сложностиПростой

Время на прочтение5 мин

Охват и читатели6

Обзор

Dataclasses появились в Python 3.7 и быстро стали стандартом: меньше бойлерплейта, чем у обычных классов, проще, чем attrs, и не требуют зависимостей. Выглядят настолько просто, что кажется, что ломаться там нечему. Но у них есть три ловушки, которые не видны при написании.

Ошибка 1: mutable default в полях

Вот этот код:

from dataclasses import dataclass

@dataclass
class UserConfig:
    name: str
    permissions: list[str] = []

...не компилируется. Python выдаёт ValueError: mutable default value и отказывается создавать класс. Это хорошо: ошибка ловится сразу, разработчик идёт в документацию и узнаёт про field(default_factory=...).

Проблема в другом. Часто разработчик «исправляет» ошибку вот так:

@dataclass
class UserConfig:
    name: str
    permissions: list[str] = None

    def __post_init__(self):
        if self.permissions is None:
            self.permissions = []

Формально работает, но permissions теперь Optional[list[str]], хотя по смыслу это всегда список. Mypy будет ругаться (или молчать с type: ignore), и каждое место использования будет либо проверять на None, либо игнорировать тип. Через полгода кто-то передаст None явно, думая, что это допустимо, и получит баг.

Правильный способ:

from dataclasses import dataclass, field

@dataclass
class UserConfig:
    name: str
    permissions: list[str] = field(default_factory=list)

default_factory вызывается при каждом создании экземпляра. Каждый UserConfig получает свой пустой список. Тип всегда list[str], никакого Optional, mypy доволен.

Для более сложных значений по умолчанию — lambda:

@dataclass
class PipelineConfig:
    name: str
    steps: list[str] = field(default_factory=lambda: ["validate", "transform", "load"])
    metadata: dict[str, str] = field(default_factory=dict)

Каждый экземпляр получает свою копию steps и свой пустой dict. Мутация одного экземпляра не затрагивает другие.

Казалось бы, мелочь. Но часто бывает так, что один экземпляр UserConfig мутировал shared-список permissions, и все остальные экземпляры (созданные до исправления, когда default был списком, а не factory) внезапно получали чужие permissions. Р

Ошибка 2: frozen=True не делает объект неизменяемым

Многие разработчики думают, что frozen=True делает dataclass иммутабельным. Это не так.

@dataclass(frozen=True)
class Report:
    title: str
    tags: list[str] = field(default_factory=list)

report = Report(title="Q1", tags=["finance"])

frozen=True запрещает присваивание в поля. report.title = "Q2" вызовет FrozenInstanceError. Пока всё логично. Но вот это работает без ошибок:

report.tags.append("urgent")
print(report.tags)  # ['finance', 'urgent'] — объект изменился!

frozen=True запрещает setattr (переназначение поля), но не запрещает мутацию содержимого поля. report.tags = new_list упадёт. report.tags.append(item) пройдёт. Python не может запретить мутацию произвольного объекта, потому что не знает, какие его методы меняют состояние, а какие нет.

Frozen-датаклассы часто используют как ключи словарей и элементы множеств, потому что frozen=True генерирует hash. И вот что получается:

cache = {}
key = Report(title="Q1", tags=["finance"])
cache[key] = "some result"

key.tags.append("urgent")  # мутируем содержимое
print(cache[key])           # KeyError! Хеш изменился, ключ потерян.

Хеш объекта вычисляется при первом обращении и зависит от содержимого полей. Когда вы мутируете список внутри frozen-объекта, хеш меняется, и словарь не может найти ключ, потому что ищет по новому хешу в бакете, куда объект попал по старому.

Если нужна настоящая иммутабельность, используйте иммутабельные типы для всех полей:

@dataclass(frozen=True)
class Report:
    title: str
    tags: tuple[str, ...]  # tuple вместо list

report = Report(title="Q1", tags=("finance",))
# report.tags.append("urgent")  # AttributeError: tuple has no append

Tuple, frozenset, str — иммутабельные. Если все поля frozen-датакласса используют такие типы, объект действительно неизменяемый. Если хотя бы одно поле мутабельное, frozen защищает только от переназначения, но не от изменения содержимого.

Правило простое: frozen-датакласс, который используется как ключ или элемент множества, должен содержать только хешируемые и иммутабельные поля. Если нужен список, оберните в tuple. Если нужен set, используйте frozenset. Если нужен dict... ну, тут сложнее, но types.MappingProxyType или просто отказ от dict в frozen-классе.

Ошибка 3: наследование ломает сравнение

Это самая неприятная из трёх ошибок, потому что результат зависит от порядка операндов.

@dataclass
class Animal:
    name: str
    weight: float

@dataclass
class Dog(Animal):
    breed: str

animal = Animal(name="Rex", weight=30.0)
dog = Dog(name="Rex", weight=30.0, breed="Labrador")

print(animal == dog)  # True
print(dog == animal)  # False

animal == dog вызывает Animal.__eq__, который сравнивает только поля Animal: name и weight. Они совпадают. Про breed Animal ничего не знает, поэтому не проверяет. Результат: True.

dog == animal вызывает Dog.__eq__, который сравнивает все три поля: name, weight, breed. У animal нет breed, Dog.eq возвращает NotImplemented, Python переворачивает и пробует Animal.eq, который опять сравнивает только name и weight. Результат: False.

a == b и b == a дают разные результаты. Это нарушает контракт равенства (симметричность), и последствия проявляются в самых неожиданных местах:

animals = {animal, dog}
print(len(animals))  # 1 или 2? Зависит от порядка вставки.

Set хеширует объекты и проверяет равенство при коллизии. Если animal добавлен первым, а dog при вставке сравнивается с ним через animal == dog (True), set решит, что это дубликат, и не добавит. Если dog первый, а animal сравнивается через dog == animal (False), оба останутся.

Исправить можно тремя способами. Первый — проверять тип в eq:

@dataclass
class Animal:
    name: str
    weight: float

    def __eq__(self, other):
        if type(other) is not type(self):
            return NotImplemented
        return (self.name, self.weight) == (other.name, other.weight)

Теперь Animal("Rex", 30) == Dog("Rex", 30, "Lab") вернёт NotImplemented с обеих сторон, и Python вернёт False. Симметрично.

Второй — использовать eq=False на родителе и генерировать eq только на дочернем классе. Третий — не наследовать датаклассы друг от друга и использовать композицию:

@dataclass
class AnimalInfo:
    name: str
    weight: float

@dataclass
class Dog:
    info: AnimalInfo
    breed: str

Третий вариант самый безопасный, потому что не создаёт проблем с eq, hash и порядком полей. Наследование датаклассов работает, но требует внимания к деталям, которые очень легко пропустить.

Ловушка с порядком полей при наследовании

Ещё одна проблема, которая связана с наследованием, но не с равенством:

@dataclass
class Base:
    name: str
    value: int = 0  # поле с дефолтом

@dataclass
class Child(Base):
    label: str  # поле без дефолта — TypeError!

Python объединяет поля в порядке MRO: name, value (с дефолтом), label (без дефолта). Поля без дефолта после полей с дефолтом запрещены (как в обычных функциях). Решение для Python 3.10+:

@dataclass
class Child(Base):
    label: str = field(kw_only=True)

child = Child(name="test", label="important")  # value=0 по умолчанию

kw_only=True делает поле keyword-only аргументом, и оно не участвует в позиционном порядке.

Итого

Три пункта, которые стоит проверить перед тем, как dataclass попадёт в прод. Все мутабельные дефолты через field(default_factory=...), никаких = [] и = {} и тем более = None с __post_init__. Если frozen=True, все поля иммутабельных типов, иначе frozen защищает только от переназначения. Если наследуете датаклассы, проверьте eq на симметричность и подумайте, не лучше ли композиция.

Если вы видели другие проблемы с dataclasses, пишите в комментариях. Спасибо, что дочитали.


Такие ошибки редко ломают проект сразу, но хорошо показывают, насколько глубоко вы понимаете Python за пределами синтаксиса.

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

Практические разборы от экспертов в разработке ищите в календаре бесплатных демо-уроков.