> **Источник:** https://python-all.ru/3.8/library/dataclasses.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# [`dataclasses`](https://python-all.ru/3.8/library/dataclasses.html#module-dataclasses) – Классы данных

**Исходный код:** [Lib/dataclasses.py](https://python-all.ru/src/3.8/Lib/dataclasses.py)

---

Этот модуль предоставляет декоратор и функции для автоматического добавления генерируемых [специальных методов](https://python-all.ru/3.8/glossary.html#term-special-method), таких как [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__) и [`__repr__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__repr__), в пользовательские классы. Изначально он описан в [**PEP 557**](https://python-all.ru/3.8/library/dataclasses.html).

Переменные-члены, используемые в этих сгенерированных методах, определяются с помощью [**PEP 526**](https://python-all.ru/3.8/library/dataclasses.html) аннотаций типов. Например, этот код:

```python
from dataclasses import dataclass

@dataclass
class InventoryItem:
    """Класс для отслеживания предмета в инвентаре."""
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand
```

Добавит, среди прочего, [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__), который выглядит так:

```python
def __init__(self, name: str, unit_price: float, quantity_on_hand: int = 0):
    self.name = name
    self.unit_price = unit_price
    self.quantity_on_hand = quantity_on_hand
```

Обратите внимание, что этот метод добавляется в класс автоматически: он не указан явно в определении `InventoryItem`, показанном выше.

Добавлено в версии 3.7.

## Декораторы, классы и функции уровня модуля

#### `@dataclasses.dataclass(*, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)`

Эта функция – [декоратор](https://python-all.ru/3.8/glossary.html#term-decorator), который используется для добавления сгенерированных [специальные методы](https://python-all.ru/3.8/glossary.html#term-special-method) в классы, как описано ниже.

Декоратор [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass) просматривает класс, чтобы найти `field`. `field` определяется как переменная класса, имеющая [аннотацию типа](https://python-all.ru/3.8/glossary.html#term-variable-annotation). За двумя исключениями, описанными ниже, ничто в [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass) не проверяет тип, указанный в аннотации переменной.

Порядок полей во всех сгенерированных методах соответствует порядку их объявления в определении класса.

Декоратор [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass) добавит в класс различные «dunder»-методы, описанные ниже. Если какой-либо из добавляемых методов уже существует в классе, поведение зависит от параметра, как описано ниже. Декоратор возвращает тот же класс, к которому применяется; новый класс не создаётся.

Если [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass) используется как простой декоратор без параметров, он ведёт себя так, как если бы использовались значения по умолчанию, описанные в этой сигнатуре. То есть следующие три варианта применения [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass) эквивалентны:

```python
@dataclass
class C:
    ...

@dataclass()
class C:
    ...

@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class C:
   ...
```

Параметры [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass):

- `init`: Если true (по умолчанию), будет сгенерирован метод [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__).

  Если класс уже определяет [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__), этот параметр игнорируется.
- `repr`: Если true (по умолчанию), будет сгенерирован метод [`__repr__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__repr__). Сгенерированная repr-строка будет содержать имя класса и имя и repr каждого поля в порядке их определения в классе. Поля, помеченные как исключённые из repr, не включаются. Например: `InventoryItem(name='widget', unit_price=3.0, quantity_on_hand=10)`.

  Если класс уже определяет [`__repr__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__repr__), этот параметр игнорируется.
- `eq`: Если true (по умолчанию), будет сгенерирован метод [`__eq__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__eq__). Этот метод сравнивает класс так, как если бы он был кортежем его полей, по порядку. Оба сравниваемых экземпляра должны быть одного типа.

  Если класс уже определяет [`__eq__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__eq__), этот параметр игнорируется.
- `order`: Если true (по умолчанию `False`), будут сгенерированы методы [`__lt__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__lt__), [`__le__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__le__), [`__gt__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__gt__) и [`__ge__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__ge__). Они сравнивают класс так, как если бы он был кортежем его полей, по порядку. Оба сравниваемых экземпляра должны быть одного типа. Если `order` равно true, а `eq` – false, возбуждается [`ValueError`](https://python-all.ru/3.8/library/exceptions.html#ValueError).

  Если класс уже определяет любой из [`__lt__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__lt__), [`__le__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__le__), [`__gt__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__gt__) или [`__ge__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__ge__), то вызывается [`TypeError`](https://python-all.ru/3.8/library/exceptions.html#TypeError).
- `unsafe_hash`: Если `False` (по умолчанию), метод [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__) генерируется в соответствии с тем, как установлены `eq` и `frozen`.

  [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__) используется встроенной [`hash()`](https://python-all.ru/3.8/library/functions.html#hash) и при добавлении объектов в хешируемые коллекции, такие как словари и множества. Наличие [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__) подразумевает, что экземпляры класса неизменяемы. Изменяемость – сложное свойство, зависящее от намерений программиста, наличия и поведения [`__eq__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__eq__), а также значений флагов `eq` и `frozen` в декораторе [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass).

  По умолчанию [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass) не будет неявно добавлять метод [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__), если это не безопасно. Он также не будет добавлять или изменять существующий явно определённый метод [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__). Установка атрибута класса `__hash__ = None` имеет для Python определённое значение, описанное в документации [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__).

  Если [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__) не определён явно или установлен в `None`, то [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass) *может* добавить неявный метод [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__). Хотя это не рекомендуется, можно принудительно заставить [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass) создать метод [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__) с помощью `unsafe_hash=True`. Это может потребоваться, если класс логически неизменяем, но всё же может быть изменён. Это специализированный случай использования, и к нему следует отнестись с осторожностью.

  Вот правила, определяющие неявное создание метода [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__). Обратите внимание, что нельзя одновременно иметь явный метод [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__) в классе данных и устанавливать `unsafe_hash=True`; это приведёт к [`TypeError`](https://python-all.ru/3.8/library/exceptions.html#TypeError).

  Если `eq` и `frozen` оба равны true, по умолчанию [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass) сгенерирует метод [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__). Если `eq` равно true, а `frozen` – false, [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__) будет установлено в `None`, что делает объект нехешируемым (каковым он и является, поскольку изменяем). Если `eq` равно false, [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__) останется без изменений, то есть будет использован метод [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__) родительского класса (если родительский класс – [`object`](https://python-all.ru/3.8/library/functions.html#object), это означает возврат к хешированию на основе id).
- `frozen`: Если true (по умолчанию `False`), присваивание полям будет вызывать исключение. Это эмулирует неизменяемые (замороженные) экземпляры. Если [`__setattr__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__setattr__) или [`__delattr__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__delattr__) определён в классе, то возбуждается [`TypeError`](https://python-all.ru/3.8/library/exceptions.html#TypeError). См. обсуждение ниже.

`field` могут опционально задавать значение по умолчанию, используя обычный синтаксис Python:

```python
@dataclass
class C:
    a: int       # 'a' не имеет значения по умолчанию
    b: int = 0   # задайте значение по умолчанию для 'b'
```

В этом примере и `a`, и `b` будут включены в добавленный метод [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__), который будет определён как:

```python
def __init__(self, a: int, b: int = 0):
```

[`TypeError`](https://python-all.ru/3.8/library/exceptions.html#TypeError) будет возбуждено, если поле без значения по умолчанию следует за полем со значением по умолчанию. Это верно как в случае, когда это происходит в одном классе, так и в результате наследования классов.

#### `dataclasses.field(*, default=MISSING, default_factory=MISSING, repr=True, hash=None, init=True, compare=True, metadata=None)`

Для простых и распространённых случаев никакой дополнительной функциональности не требуется. Однако у dataclass есть некоторые возможности, для которых нужна дополнительная информация о полях. Чтобы восполнить эту потребность, можно заменить значение поля по умолчанию на вызов предоставленной функции [`field()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.field). Например:

```python
@dataclass
class C:
    mylist: List[int] = field(default_factory=list)

c = C()
c.mylist += [1, 2, 3]
```

Как показано выше, значение `MISSING` – это сигнальный объект, используемый для обнаружения, предоставлены ли параметры `default` и `default_factory`. Этот сигнал используется потому, что `None` является допустимым значением для `default`. Никакой код не должен напрямую использовать значение `MISSING`.

Параметры [`field()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.field):

- `default`: Если указано, это будет значением по умолчанию для данного поля. Это необходимо, потому что сам вызов [`field()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.field) заменяет обычную позицию значения по умолчанию.
- `default_factory`: Если указано, это должен быть вызываемый объект без аргументов, который будет вызван, когда потребуется значение по умолчанию для этого поля. Помимо прочего, это можно использовать для указания полей с изменяемыми значениями по умолчанию, как обсуждается ниже. Указывать одновременно `default` и `default_factory` – ошибка.
- `init`: Если true (по умолчанию), это поле включается в качестве параметра в сгенерированный метод [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__).
- `repr`: Если true (по умолчанию), это поле включается в строку, возвращаемую сгенерированным методом [`__repr__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__repr__).
- `compare`: Если true (по умолчанию), это поле включается в сгенерированные методы равенства и сравнения ([`__eq__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__eq__), [`__gt__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__gt__) и др.).
- `hash`: Это может быть bool или `None`. Если true, это поле включается в сгенерированный метод [`__hash__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__hash__). Если `None` (по умолчанию), используется значение `compare`: обычно это ожидаемое поведение. Поле должно учитываться в хеше, если оно используется для сравнений. Установка этого значения в любое значение, отличное от `None`, не рекомендуется.

  Одна из возможных причин установить `hash=False`, но `compare=True` – если вычисление хэш-значения для поля дорого, это поле нужно для проверки равенства, а есть другие поля, которые вносят вклад в хэш типа. Даже если поле исключено из хэша, оно всё равно будет использоваться для сравнений.
- `metadata`: Это может быть отображение или None. None обрабатывается как пустой словарь. Это значение оборачивается в [`MappingProxyType()`](https://python-all.ru/3.8/library/types.html#types.MappingProxyType) для обеспечения только для чтения и предоставляется в объекте [`Field`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.Field). Оно вообще не используется Data Classes и предоставляется как механизм расширения сторонними библиотеками. Несколько сторонних библиотек могут иметь свои собственные ключи для использования в качестве пространства имён в метаданных.

Если значение поля по умолчанию задаётся вызовом [`field()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.field), то атрибут класса для этого поля будет заменён указанным значением `default`. Если `default` не указан, атрибут класса будет удалён. Предполагается, что после выполнения декоратора [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass) атрибуты класса будут содержать значения по умолчанию для полей, как если бы было указано само значение по умолчанию. Например, после:

```python
@dataclass
class C:
    x: int
    y: int = field(repr=False)
    z: int = field(repr=False, default=10)
    t: int = 20
```

Атрибут класса `C.z` будет равен `10`, атрибут класса `C.t` будет равен `20`, а атрибуты класса `C.x` и `C.y` не будут установлены.

#### `class dataclasses.Field`

Объекты [`Field`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.Field) описывают каждое определённое поле. Эти объекты создаются внутренне и возвращаются методом уровня модуля [`fields()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.fields) (см. ниже). Пользователи никогда не должны создавать экземпляр [`Field`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.Field) напрямую. Его документированные атрибуты:

> - `name`: Имя поля.
> - `type`: Тип поля.
> - `default`, `default_factory`, `init`, `repr`, `hash`, `compare` и `metadata` имеют одинаковый смысл и значения, как и в объявлении [`field()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.field).

Могут существовать и другие атрибуты, но они приватные, и на них не следует полагаться или проверять их.

#### `dataclasses.fields(class_or_instance)`

Возвращает кортеж объектов [`Field`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.Field), определяющих поля этого датакласса. Принимает либо датакласс, либо экземпляр датакласса. Вызывает [`TypeError`](https://python-all.ru/3.8/library/exceptions.html#TypeError), если передан не датакласс и не его экземпляр. Не возвращает псевдополя, которые являются `ClassVar` или `InitVar`.

#### `dataclasses.asdict(instance, *, dict_factory=dict)`

Преобразует dataclass `instance` в словарь (используя фабричную функцию `dict_factory`). Каждый dataclass преобразуется в словарь своих полей в виде пар `name: value`. dataclasses, словари, списки и кортежи обходятся рекурсивно. Например:

```python
@dataclass
class Point:
     x: int
     y: int

@dataclass
class C:
     mylist: List[Point]

p = Point(10, 20)
assert asdict(p) == {'x': 10, 'y': 20}

c = C([Point(0, 0), Point(10, 4)])
assert asdict(c) == {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}
```

Вызывает [`TypeError`](https://python-all.ru/3.8/library/exceptions.html#TypeError), если `instance` не является экземпляром dataclass.

#### `dataclasses.astuple(instance, *, tuple_factory=tuple)`

Преобразует dataclass `instance` в кортеж (используя фабричную функцию `tuple_factory`). Каждый dataclass преобразуется в кортеж значений своих полей. dataclasses, словари, списки и кортежи обходятся рекурсивно.

Продолжая предыдущий пример:

```python
assert astuple(p) == (10, 20)
assert astuple(c) == ([(0, 0), (10, 4)],)
```

Вызывает [`TypeError`](https://python-all.ru/3.8/library/exceptions.html#TypeError), если `instance` не является экземпляром dataclass.

#### `dataclasses.make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)`

Создаёт новый dataclass с именем `cls_name`, полями, определёнными в `fields`, базовыми классами, заданными в `bases`, и инициализированным пространством имён, заданным в `namespace`. `fields` – это итерируемый объект, каждый элемент которого является `name`, `(name, type)` или `(name, type, Field)`. Если передано только `name`, то для `type` используется `typing.Any`. Значения `init`, `repr`, `eq`, `order`, `unsafe_hash` и `frozen` имеют тот же смысл, что и в [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass).

Эта функция не является строго обязательной, поскольку любой механизм Python для создания нового класса с помощью `__annotations__` может затем применить функцию [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass) для преобразования этого класса в датакласс. Эта функция предоставляется для удобства. Например:

```python
C = make_dataclass('C',
                   [('x', int),
                     'y',
                    ('z', int, field(default=5))],
                   namespace={'add_one': lambda self: self.x + 1})
```

Эквивалентно:

```python
@dataclass
class C:
    x: int
    y: 'typing.Any'
    z: int = 5

    def add_one(self):
        return self.x + 1
```

#### `dataclasses.replace(instance, **changes)`

Создаёт новый объект того же типа, что и `instance`, заменяя поля значениями из `changes`. Если `instance` не является Data Class, вызывает [`TypeError`](https://python-all.ru/3.8/library/exceptions.html#TypeError). Если значения в `changes` не задают полей, вызывает [`TypeError`](https://python-all.ru/3.8/library/exceptions.html#TypeError).

Новый возвращаемый объект создаётся вызовом метода [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__) датакласса. Это гарантирует, что `__post_init__()`, если он присутствует, также будет вызван.

Переменные, предназначенные только для инициализации, без значений по умолчанию (если таковые имеются) должны быть указаны в вызове [`replace()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.replace), чтобы их можно было передать в [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__) и `__post_init__()`.

Ошибкой считается, если `changes` содержит какие-либо поля, определённые как имеющие `init=False`. В этом случае будет возбуждено [`ValueError`](https://python-all.ru/3.8/library/exceptions.html#ValueError).

Следует заранее знать, как работают поля `init=False` при вызове [`replace()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.replace). Они не копируются из исходного объекта, а инициализируются в `__post_init__()`, если вообще инициализируются. Ожидается, что поля `init=False` будут использоваться редко и осмотрительно. Если они используются, возможно, разумно иметь альтернативные конструкторы класса или, возможно, пользовательский метод `replace()` (или с похожим именем), который обрабатывает копирование экземпляра.

#### `dataclasses.is_dataclass(class_or_instance)`

Возвращает `True`, если его параметр является dataclass или экземпляром dataclass, иначе возвращает `False`.

Если нужно узнать, является ли класс экземпляром dataclass (а не самим dataclass), добавьте дополнительную проверку на `not isinstance(obj, type)`:

```python
def is_dataclass_instance(obj):
    return is_dataclass(obj) and not isinstance(obj, type)
```

## Обработка после инициализации

Сгенерированный код [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__) вызовет метод с именем `__post_init__()`, если `__post_init__()` определён в классе. Обычно он вызывается как `self.__post_init__()`. Однако, если определены какие-либо поля `InitVar`, они также будут переданы в `__post_init__()` в порядке их определения в классе. Если метод [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__) не сгенерирован, то `__post_init__()` не будет вызываться автоматически.

Среди прочего, это позволяет инициализировать значения полей, которые зависят от одного или нескольких других полей. Например:

```python
@dataclass
class C:
    a: float
    b: float
    c: float = field(init=False)

    def __post_init__(self):
        self.c = self.a + self.b
```

Смотрите раздел ниже о переменных, используемых только для инициализации, чтобы узнать о способах передачи параметров в `__post_init__()`. Также обратите внимание на предупреждение о том, как [`replace()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.replace) обрабатывает поля `init=False`.

## Переменные класса

Одно из двух мест, где [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass) действительно проверяет тип поля – это определение, является ли поле переменной класса, как определено в [**PEP 526**](https://python-all.ru/3.8/library/dataclasses.html). Это делается путём проверки, является ли тип поля `typing.ClassVar`. Если поле является `ClassVar`, оно исключается из рассмотрения как поле и игнорируется механизмами dataclass. Такие `ClassVar` псевдополя не возвращаются функцией модульного уровня [`fields()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.fields).

## Переменные только для инициализации

Другое место, где [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass) проверяет аннотацию типа – это определение, является ли поле переменной только для инициализации (init-only). Это делается путём проверки, является ли тип поля типом `dataclasses.InitVar`. Если поле является `InitVar`, оно считается псевдополем, называемым init-only полем. Поскольку это не настоящее поле, оно не возвращается функцией модульного уровня [`fields()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.fields). Init-only поля добавляются как параметры в сгенерированный метод [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__) и передаются необязательному методу `__post_init__()`. В остальном они не используются dataclasses.

Например, предположим, что поле будет инициализироваться из базы данных, если значение не указано при создании класса:

```python
@dataclass
class C:
    i: int
    j: int = None
    database: InitVar[DatabaseType] = None

    def __post_init__(self, database):
        if self.j is None and database is not None:
            self.j = database.lookup('j')

c = C(10, database=my_database)
```

В этом случае [`fields()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.fields) вернёт объекты [`Field`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.Field) для `i` и `j`, но не для `database`.

## Замороженные экземпляры

Создать по-настоящему неизменяемые объекты Python невозможно. Однако, передав `frozen=True` декоратору [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass), можно эмулировать неизменяемость. В этом случае dataclasses добавит в класс методы [`__setattr__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__setattr__) и [`__delattr__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__delattr__). При вызове эти методы вызовут исключение [`FrozenInstanceError`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.FrozenInstanceError).

Использование `frozen=True` сопряжено с небольшим снижением производительности: [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__) не может использовать простое присваивание для инициализации полей и должен использовать [`object.__setattr__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__setattr__).

## Наследование

Когда dataclass создаётся декоратором [`dataclass()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.dataclass), он просматривает все базовые классы в порядке, обратном MRO (начиная с [`object`](https://python-all.ru/3.8/library/functions.html#object)), и для каждого найденного dataclass добавляет поля из этого базового класса в упорядоченное отображение полей. После добавления всех полей базовых классов он добавляет свои собственные поля в это упорядоченное отображение. Все сгенерированные методы будут использовать это объединённое вычисленное упорядоченное отображение полей. Поскольку поля располагаются в порядке вставки, производные классы переопределяют базовые. Пример:

```python
@dataclass
class Base:
    x: Any = 15.0
    y: int = 0

@dataclass
class C(Base):
    z: int = 10
    x: int = 15
```

Итоговый список полей в порядке: `x`, `y`, `z`. Итоговый тип `x` – `int`, как указано в классе `C`.

Сгенерированный метод [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__) для `C` будет выглядеть так:

```python
def __init__(self, x: int = 15, y: int = 0, z: int = 10):
```

## Функции фабрики по умолчанию

> Если [`field()`](https://python-all.ru/3.8/library/dataclasses.html#dataclasses.field) указывает `default_factory`, то он вызывается без аргументов, когда требуется значение по умолчанию для поля. Например, чтобы создать новый экземпляр списка, используйте:
>
> ```python
> mylist: list = field(default_factory=list)
> ```
>
> Если поле исключено из [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__) (с помощью `init=False`) и поле также указывает `default_factory`, то функция фабрики значений по умолчанию всегда будет вызываться из сгенерированной функции [`__init__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__init__). Это происходит потому, что нет другого способа задать полю начальное значение.

## Изменяемые значения по умолчанию

> Python хранит значения переменных-членов по умолчанию в атрибутах класса. Рассмотрим этот пример без использования dataclasses:
>
> ```python
> class C:
>     x = []
>     def add(self, element):
>         self.x.append(element)
>
> o1 = C()
> o2 = C()
> o1.add(1)
> o2.add(2)
> assert o1.x == [1, 2]
> assert o1.x is o2.x
> ```
>
> Обратите внимание, что два экземпляра класса `C` разделяют одну и ту же переменную класса `x`, как и ожидалось.
>
> При использовании dataclasses, *если* бы этот код был корректным:
>
> ```python
> @dataclass
> class D:
>     x: List = []
>     def add(self, element):
>         self.x += element
> ```
>
> он бы сгенерировал код, похожий на:
>
> ```python
> class D:
>     x = []
>     def __init__(self, x=x):
>         self.x = x
>     def add(self, element):
>         self.x += element
>
> assert D().x is D().x
> ```
>
> Это имеет ту же проблему, что и исходный пример с классом `C`. А именно, два экземпляра класса `D`, которые не указывают значение для `x` при создании экземпляра класса, будут совместно использовать одну копию `x`. Поскольку dataclasses используют обычное создание классов Python, они также разделяют это поведение. Не существует общего способа для Data Classes обнаружить это условие. Вместо этого dataclasses вызовут [`TypeError`](https://python-all.ru/3.8/library/exceptions.html#TypeError), если обнаружат параметр по умолчанию типа `list`, `dict` или `set`. Это частичное решение, но оно защищает от многих распространённых ошибок.
>
> Использование фабричных функций по умолчанию – это способ создавать новые экземпляры изменяемых типов в качестве значений полей по умолчанию:
>
> ```python
> @dataclass
> class D:
>     x: list = field(default_factory=list)
>
> assert D().x is not D().x
> ```

## Исключения

#### `exception dataclasses.FrozenInstanceError`

Возбуждается, когда неявно определённые [`__setattr__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__setattr__) или [`__delattr__()`](https://python-all.ru/3.8/reference/datamodel.html#object.__delattr__) вызываются для dataclass, определённого с `frozen=True`. Является подклассом [`AttributeError`](https://python-all.ru/3.8/library/exceptions.html#AttributeError).
