Документация Python неофициальный перевод

dataclasses.md

433 строк · 43.2 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.9/library/dataclasses.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# [`dataclasses`](https://python-all.ru/3.9/library/dataclasses.html#module-dataclasses) – Классы данных89**Исходный код:** [Lib/dataclasses.py](https://python-all.ru/src/3.9/Lib/dataclasses.py)1011---1213Этот модуль предоставляет декоратор и функции для автоматического добавления генерируемых [специальных методов](https://python-all.ru/3.9/glossary.html#term-special-method), таких как [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__) и [`__repr__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__repr__), в пользовательские классы. Изначально он описан в [**PEP 557**](https://python-all.ru/3.9/library/dataclasses.html).1415Переменные-члены, используемые в этих сгенерированных методах, определяются с помощью аннотаций типов [**PEP 526**](https://python-all.ru/3.9/library/dataclasses.html). Например, следующий код:1617```python18from dataclasses import dataclass1920@dataclass21class InventoryItem:22    """Класс для отслеживания предмета в инвентаре."""23    name: str24    unit_price: float25    quantity_on_hand: int = 02627    def total_cost(self) -> float:28        return self.unit_price * self.quantity_on_hand29```3031добавит, среди прочего, метод [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__), который будет выглядеть так:3233```python34def __init__(self, name: str, unit_price: float, quantity_on_hand: int = 0):35    self.name = name36    self.unit_price = unit_price37    self.quantity_on_hand = quantity_on_hand38```3940Обратите внимание, что этот метод добавляется в класс автоматически: он не указан явно в определении `InventoryItem`, показанном выше.4142Добавлено в версии 3.7.4344## Декораторы, классы и функции уровня модуля4546#### `@dataclasses.dataclass(*, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)`4748Эта функция – [декоратор](https://python-all.ru/3.9/glossary.html#term-decorator), который используется для добавления сгенерированных [специальные методы](https://python-all.ru/3.9/glossary.html#term-special-method) в классы, как описано ниже.4950Декоратор [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass) просматривает класс в поисках `field`. `field` определяется как переменная класса, имеющая [аннотацию типа](https://python-all.ru/3.9/glossary.html#term-variable-annotation). За двумя исключениями, описанными ниже, в [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass) тип, указанный в аннотации переменной, не проверяется.5152Порядок полей во всех сгенерированных методах соответствует порядку их объявления в определении класса.5354Декоратор [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass) добавит в класс различные «дандер»-методы, описанные ниже. Если какой-либо из добавляемых методов уже существует в классе, поведение зависит от параметра, как описано ниже. Декоратор возвращает тот же класс, к которому он был применён; новый класс не создаётся.5556Если [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass) используется как простой декоратор без параметров, он ведёт себя так, как если бы использовались значения по умолчанию, описанные в этой сигнатуре. То есть следующие три варианта применения [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass) эквивалентны:5758```python59@dataclass60class C:61    ...6263@dataclass()64class C:65    ...6667@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)68class C:69   ...70```7172Параметры [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass):7374- `init`: Если true (по умолчанию), будет сгенерирован метод [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__).7576  Если класс уже определяет [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__), этот параметр игнорируется.77- `repr`: Если true (по умолчанию), будет сгенерирован метод [`__repr__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__repr__). Сгенерированная repr-строка будет содержать имя класса и имя и repr каждого поля в порядке их определения в классе. Поля, помеченные как исключённые из repr, не включаются. Например: `InventoryItem(name='widget', unit_price=3.0, quantity_on_hand=10)`.7879  Если класс уже определяет [`__repr__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__repr__), этот параметр игнорируется.80- `eq`: Если true (по умолчанию), будет сгенерирован метод [`__eq__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__eq__). Этот метод сравнивает класс так, как если бы он был кортежем его полей, по порядку. Оба сравниваемых экземпляра должны быть одного типа.8182  Если класс уже определяет [`__eq__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__eq__), этот параметр игнорируется.83- `order`: Если true (по умолчанию `False`), будут сгенерированы методы [`__lt__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__lt__), [`__le__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__le__), [`__gt__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__gt__) и [`__ge__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__ge__). Они сравнивают класс так, как если бы он был кортежем его полей, по порядку. Оба сравниваемых экземпляра должны быть одного типа. Если `order` равно true, а `eq` – false, возбуждается [`ValueError`](https://python-all.ru/3.9/library/exceptions.html#ValueError).8485  Если класс уже определяет любой из [`__lt__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__lt__), [`__le__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__le__), [`__gt__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__gt__) или [`__ge__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__ge__), то вызывается [`TypeError`](https://python-all.ru/3.9/library/exceptions.html#TypeError).86- `unsafe_hash`: Если `False` (по умолчанию), метод [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__) генерируется в соответствии с тем, как установлены `eq` и `frozen`.8788  [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__) используется встроенной [`hash()`](https://python-all.ru/3.9/library/functions.html#hash) и при добавлении объектов в хешируемые коллекции, такие как словари и множества. Наличие [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__) подразумевает, что экземпляры класса неизменяемы. Изменяемость – сложное свойство, зависящее от намерений программиста, наличия и поведения [`__eq__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__eq__), а также значений флагов `eq` и `frozen` в декораторе [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass).8990  По умолчанию [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass) не будет неявно добавлять метод [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__), если это не безопасно. Он также не будет добавлять или изменять существующий явно определённый метод [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__). Установка атрибута класса `__hash__ = None` имеет для Python определённое значение, описанное в документации [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__).9192  Если [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__) не определён явно или установлен в `None`, то [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass) *может* добавить неявный метод [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__). Хотя это не рекомендуется, можно принудительно заставить [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass) создать метод [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__) с помощью `unsafe_hash=True`. Это может потребоваться, если класс логически неизменяем, но всё же может быть изменён. Это специализированный случай использования, и к нему следует отнестись с осторожностью.9394  Вот правила, определяющие неявное создание метода [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__). Обратите внимание, что нельзя одновременно иметь явный метод [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__) в классе данных и устанавливать `unsafe_hash=True`; это приведёт к [`TypeError`](https://python-all.ru/3.9/library/exceptions.html#TypeError).9596  Если `eq` и `frozen` оба равны true, по умолчанию [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass) сгенерирует метод [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__). Если `eq` равно true, а `frozen` – false, [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__) будет установлено в `None`, что делает объект нехешируемым (каковым он и является, поскольку изменяем). Если `eq` равно false, [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__) останется без изменений, то есть будет использован метод [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__) родительского класса (если родительский класс – [`object`](https://python-all.ru/3.9/library/functions.html#object), это означает возврат к хешированию на основе id).97- `frozen`: Если true (по умолчанию `False`), присваивание полям будет вызывать исключение. Это эмулирует неизменяемые (замороженные) экземпляры. Если [`__setattr__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__setattr__) или [`__delattr__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__delattr__) определён в классе, то возбуждается [`TypeError`](https://python-all.ru/3.9/library/exceptions.html#TypeError). См. обсуждение ниже.9899`field` могут опционально задавать значение по умолчанию, используя обычный синтаксис Python:100101```python102@dataclass103class C:104    a: int       # 'a' не имеет значения по умолчанию105    b: int = 0   # задайте значение по умолчанию для 'b'106```107108В этом примере и `a`, и `b` будут включены в добавленный метод [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__), который будет определён как:109110```python111def __init__(self, a: int, b: int = 0):112```113114[`TypeError`](https://python-all.ru/3.9/library/exceptions.html#TypeError) будет возбуждено, если поле без значения по умолчанию следует за полем со значением по умолчанию. Это верно как для одного класса, так и в случае наследования классов.115116#### `dataclasses.field(*, default=MISSING, default_factory=MISSING, repr=True, hash=None, init=True, compare=True, metadata=None)`117118Для простых и распространённых случаев никакой дополнительной функциональности не требуется. Однако у dataclass есть некоторые возможности, для которых нужна дополнительная информация о полях. Чтобы восполнить эту потребность, можно заменить значение поля по умолчанию на вызов предоставленной функции [`field()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.field). Например:119120```python121@dataclass122class C:123    mylist: list[int] = field(default_factory=list)124125c = C()126c.mylist += [1, 2, 3]127```128129Как показано выше, значение `MISSING` – это сигнальный объект, используемый для обнаружения, предоставлены ли параметры `default` и `default_factory`. Этот сигнал используется потому, что `None` является допустимым значением для `default`. Никакой код не должен напрямую использовать значение `MISSING`.130131Параметры [`field()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.field):132133- `default`: Если указано, это будет значением по умолчанию для данного поля. Это необходимо, потому что сам вызов [`field()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.field) заменяет обычную позицию значения по умолчанию.134- `default_factory`: Если указано, это должен быть вызываемый объект без аргументов, который будет вызван, когда потребуется значение по умолчанию для этого поля. Помимо прочего, это можно использовать для указания полей с изменяемыми значениями по умолчанию, как обсуждается ниже. Указывать одновременно `default` и `default_factory` – ошибка.135- `init`: Если true (по умолчанию), это поле включается в качестве параметра в сгенерированный метод [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__).136- `repr`: Если true (по умолчанию), это поле включается в строку, возвращаемую сгенерированным методом [`__repr__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__repr__).137- `compare`: Если true (по умолчанию), это поле включается в сгенерированные методы равенства и сравнения ([`__eq__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__eq__), [`__gt__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__gt__) и др.).138- `hash`: Это может быть bool или `None`. Если true, это поле включается в сгенерированный метод [`__hash__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__hash__). Если `None` (по умолчанию), используется значение `compare`: обычно это ожидаемое поведение. Поле должно учитываться в хеше, если оно используется для сравнений. Установка этого значения в любое значение, отличное от `None`, не рекомендуется.139140  Одна из возможных причин установить `hash=False`, но `compare=True` – если вычисление хэш-значения для поля дорого, это поле нужно для проверки равенства, а есть другие поля, которые вносят вклад в хэш типа. Даже если поле исключено из хэша, оно всё равно будет использоваться для сравнений.141- `metadata`: Это может быть отображение или None. None обрабатывается как пустой словарь. Это значение оборачивается в [`MappingProxyType()`](https://python-all.ru/3.9/library/types.html#types.MappingProxyType) для обеспечения только для чтения и предоставляется в объекте [`Field`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.Field). Оно вообще не используется Data Classes и предоставляется как механизм расширения сторонними библиотеками. Несколько сторонних библиотек могут иметь свои собственные ключи для использования в качестве пространства имён в метаданных.142143Если значение поля по умолчанию задаётся вызовом [`field()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.field), то атрибут класса для этого поля будет заменён указанным значением `default`. Если `default` не указан, атрибут класса будет удалён. Предполагается, что после выполнения декоратора [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass) атрибуты класса будут содержать значения по умолчанию для полей, как если бы было указано само значение по умолчанию. Например, после:144145```python146@dataclass147class C:148    x: int149    y: int = field(repr=False)150    z: int = field(repr=False, default=10)151    t: int = 20152```153154Атрибут класса `C.z` будет равен `10`, атрибут класса `C.t` будет равен `20`, а атрибуты класса `C.x` и `C.y` не будут установлены.155156#### `class dataclasses.Field`157158Объекты [`Field`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.Field) описывают каждое определённое поле. Эти объекты создаются внутренне и возвращаются методом уровня модуля [`fields()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.fields) (см. ниже). Пользователи никогда не должны создавать экземпляр [`Field`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.Field) напрямую. Его документированные атрибуты:159160> - `name`: Имя поля.161> - `type`: Тип поля.162> - `default`, `default_factory`, `init`, `repr`, `hash`, `compare` и `metadata` имеют одинаковый смысл и значения, как и в объявлении [`field()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.field).163164Могут существовать и другие атрибуты, но они приватные, и на них не следует полагаться или проверять их.165166#### `dataclasses.fields(class_or_instance)`167168Возвращает кортеж объектов [`Field`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.Field), определяющих поля этого датакласса. Принимает либо датакласс, либо экземпляр датакласса. Вызывает [`TypeError`](https://python-all.ru/3.9/library/exceptions.html#TypeError), если передан не датакласс и не его экземпляр. Не возвращает псевдополя, которые являются `ClassVar` или `InitVar`.169170#### `dataclasses.asdict(obj, *, dict_factory=dict)`171172Преобразует dataclass `obj` в словарь (с помощью фабричной функции `dict_factory`). Каждый dataclass преобразуется в словарь своих полей в виде пар `name: value`. Рекурсивно обрабатываются dataclass'ы, словари, списки и кортежи. Другие объекты копируются с помощью [`copy.deepcopy()`](https://python-all.ru/3.9/library/copy.html#copy.deepcopy).173174Пример использования [`asdict()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.asdict) на вложенных датаклассах:175176```python177@dataclass178class Point:179     x: int180     y: int181182@dataclass183class C:184     mylist: list[Point]185186p = Point(10, 20)187assert asdict(p) == {'x': 10, 'y': 20}188189c = C([Point(0, 0), Point(10, 4)])190assert asdict(c) == {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}191```192193Для создания поверхностной копии можно использовать следующий обходной путь:194195```python196dict((field.name, getattr(obj, field.name)) for field in fields(obj))197```198199[`asdict()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.asdict) возбуждает [`TypeError`](https://python-all.ru/3.9/library/exceptions.html#TypeError), если `obj` не является экземпляром dataclass.200201#### `dataclasses.astuple(obj, *, tuple_factory=tuple)`202203Преобразует dataclass `obj` в кортеж (с помощью фабричной функции `tuple_factory`). Каждый dataclass преобразуется в кортеж значений своих полей. Рекурсивно обрабатываются dataclass'ы, словари, списки и кортежи. Другие объекты копируются с помощью [`copy.deepcopy()`](https://python-all.ru/3.9/library/copy.html#copy.deepcopy).204205Продолжая предыдущий пример:206207```python208assert astuple(p) == (10, 20)209assert astuple(c) == ([(0, 0), (10, 4)],)210```211212Для создания поверхностной копии можно использовать следующий обходной путь:213214```python215tuple(getattr(obj, field.name) for field in dataclasses.fields(obj))216```217218[`astuple()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.astuple) возбуждает [`TypeError`](https://python-all.ru/3.9/library/exceptions.html#TypeError), если `obj` не является экземпляром dataclass.219220#### `dataclasses.make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)`221222Создаёт новый 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.9/library/dataclasses.html#dataclasses.dataclass).223224Эта функция не является строго обязательной, поскольку любой механизм Python для создания нового класса с помощью `__annotations__` может затем применить функцию [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass) для преобразования этого класса в датакласс. Эта функция предоставляется для удобства. Например:225226```python227C = make_dataclass('C',228                   [('x', int),229                     'y',230                    ('z', int, field(default=5))],231                   namespace={'add_one': lambda self: self.x + 1})232```233234Эквивалентно:235236```python237@dataclass238class C:239    x: int240    y: 'typing.Any'241    z: int = 5242243    def add_one(self):244        return self.x + 1245```246247#### `dataclasses.replace(obj, /, **changes)`248249Создаёт новый объект того же типа, что и `obj`, заменяя поля значениями из `changes`. Если `obj` не является Data Class, возбуждает [`TypeError`](https://python-all.ru/3.9/library/exceptions.html#TypeError). Если значения в `changes` не указывают поля, возбуждает [`TypeError`](https://python-all.ru/3.9/library/exceptions.html#TypeError).250251Новый возвращаемый объект создаётся вызовом метода [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__) датакласса. Это гарантирует, что `__post_init__()`, если он присутствует, также будет вызван.252253Переменные, предназначенные только для инициализации, без значений по умолчанию (если таковые имеются) должны быть указаны в вызове [`replace()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.replace), чтобы их можно было передать в [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__) и `__post_init__()`.254255Ошибкой считается, если `changes` содержит какие-либо поля, определённые как имеющие `init=False`. В этом случае будет возбуждено [`ValueError`](https://python-all.ru/3.9/library/exceptions.html#ValueError).256257Следует заранее знать, как работают поля `init=False` при вызове [`replace()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.replace). Они не копируются из исходного объекта, а инициализируются в `__post_init__()`, если вообще инициализируются. Ожидается, что поля `init=False` будут использоваться редко и осмотрительно. Если они используются, возможно, разумно иметь альтернативные конструкторы класса или, возможно, пользовательский метод `replace()` (или с похожим именем), который обрабатывает копирование экземпляра.258259#### `dataclasses.is_dataclass(obj)`260261Возвращает `True`, если его параметр является dataclass или экземпляром dataclass, иначе возвращает `False`.262263Если нужно узнать, является ли класс экземпляром dataclass (а не самим dataclass), добавьте дополнительную проверку на `not isinstance(obj, type)`:264265```python266def is_dataclass_instance(obj):267    return is_dataclass(obj) and not isinstance(obj, type)268```269270## Обработка после инициализации271272Сгенерированный код [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__) вызовет метод с именем `__post_init__()`, если `__post_init__()` определён в классе. Обычно он вызывается как `self.__post_init__()`. Однако, если определены какие-либо поля `InitVar`, они также будут переданы в `__post_init__()` в порядке их определения в классе. Если метод [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__) не сгенерирован, то `__post_init__()` не будет вызываться автоматически.273274Среди прочего, это позволяет инициализировать значения полей, которые зависят от одного или нескольких других полей. Например:275276```python277@dataclass278class C:279    a: float280    b: float281    c: float = field(init=False)282283    def __post_init__(self):284        self.c = self.a + self.b285```286287Метод [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__), сгенерированный [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass), не вызывает методы [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__) базового класса. Если базовый класс имеет метод [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__), который необходимо вызвать, обычно этот метод вызывается в методе `__post_init__()`:288289```python290@dataclass291class Rectangle:292    height: float293    width: float294295@dataclass296class Square(Rectangle):297    side: float298299    def __post_init__(self):300        super().__init__(self.side, self.side)301```302303Однако обратите внимание, что в целом вызывать сгенерированные dataclass методы [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__) не требуется, поскольку производный dataclass сам позаботится об инициализации всех полей любого базового класса, который сам является dataclass.304305Смотрите раздел ниже о переменных, используемых только для инициализации, чтобы узнать о способах передачи параметров в `__post_init__()`. Также обратите внимание на предупреждение о том, как [`replace()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.replace) обрабатывает поля `init=False`.306307## Переменные класса308309Одно из двух мест, где [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass) действительно проверяет тип поля – это определение, является ли поле переменной класса, как определено в [**PEP 526**](https://python-all.ru/3.9/library/dataclasses.html). Это делается путём проверки, является ли тип поля `typing.ClassVar`. Если поле является `ClassVar`, оно исключается из рассмотрения как поле и игнорируется механизмами dataclass. Такие `ClassVar` псевдополя не возвращаются функцией модульного уровня [`fields()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.fields).310311## Переменные только для инициализации312313Другое место, где [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass) проверяет аннотацию типа – это определение, является ли поле переменной только для инициализации (init-only). Это делается путём проверки, является ли тип поля типом `dataclasses.InitVar`. Если поле является `InitVar`, оно считается псевдополем, называемым init-only полем. Поскольку это не настоящее поле, оно не возвращается функцией модульного уровня [`fields()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.fields). Init-only поля добавляются как параметры в сгенерированный метод [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__) и передаются необязательному методу `__post_init__()`. В остальном они не используются dataclasses.314315Например, предположим, что поле будет инициализироваться из базы данных, если значение не указано при создании класса:316317```python318@dataclass319class C:320    i: int321    j: int = None322    database: InitVar[DatabaseType] = None323324    def __post_init__(self, database):325        if self.j is None and database is not None:326            self.j = database.lookup('j')327328c = C(10, database=my_database)329```330331В этом случае [`fields()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.fields) вернёт объекты [`Field`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.Field) для `i` и `j`, но не для `database`.332333## Замороженные экземпляры334335Создать по-настоящему неизменяемые объекты Python невозможно. Однако, передав `frozen=True` декоратору [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass), можно эмулировать неизменяемость. В этом случае dataclasses добавит в класс методы [`__setattr__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__setattr__) и [`__delattr__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__delattr__). При вызове эти методы вызовут исключение [`FrozenInstanceError`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.FrozenInstanceError).336337Использование `frozen=True` сопряжено с небольшим снижением производительности: [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__) не может использовать простое присваивание для инициализации полей и должен использовать [`object.__setattr__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__setattr__).338339## Наследование340341Когда dataclass создаётся декоратором [`dataclass()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.dataclass), он просматривает все базовые классы в порядке, обратном MRO (начиная с [`object`](https://python-all.ru/3.9/library/functions.html#object)), и для каждого найденного dataclass добавляет поля из этого базового класса в упорядоченное отображение полей. После добавления всех полей базовых классов он добавляет свои собственные поля в это упорядоченное отображение. Все сгенерированные методы будут использовать это объединённое вычисленное упорядоченное отображение полей. Поскольку поля располагаются в порядке вставки, производные классы переопределяют базовые. Пример:342343```python344@dataclass345class Base:346    x: Any = 15.0347    y: int = 0348349@dataclass350class C(Base):351    z: int = 10352    x: int = 15353```354355Итоговый список полей в порядке: `x`, `y`, `z`. Итоговый тип `x``int`, как указано в классе `C`.356357Сгенерированный метод [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__) для `C` будет выглядеть так:358359```python360def __init__(self, x: int = 15, y: int = 0, z: int = 10):361```362363## Функции фабрики по умолчанию364365> Если [`field()`](https://python-all.ru/3.9/library/dataclasses.html#dataclasses.field) указывает `default_factory`, то он вызывается без аргументов, когда требуется значение по умолчанию для поля. Например, чтобы создать новый экземпляр списка, используйте:366>367> ```python368> mylist: list = field(default_factory=list)369> ```370>371> Если поле исключено из [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__) (с помощью `init=False`) и поле также указывает `default_factory`, то функция фабрики значений по умолчанию всегда будет вызываться из сгенерированной функции [`__init__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__init__). Это происходит потому, что нет другого способа задать полю начальное значение.372373## Изменяемые значения по умолчанию374375> Python хранит значения переменных-членов по умолчанию в атрибутах класса. Рассмотрим этот пример без использования dataclasses:376>377> ```python378> class C:379>     x = []380>     def add(self, element):381>         self.x.append(element)382>383> o1 = C()384> o2 = C()385> o1.add(1)386> o2.add(2)387> assert o1.x == [1, 2]388> assert o1.x is o2.x389> ```390>391> Обратите внимание, что два экземпляра класса `C` разделяют одну и ту же переменную класса `x`, как и ожидалось.392>393> При использовании dataclasses, *если* бы этот код был корректным:394>395> ```python396> @dataclass397> class D:398>     x: List = []399>     def add(self, element):400>         self.x += element401> ```402>403> он бы сгенерировал код, похожий на:404>405> ```python406> class D:407>     x = []408>     def __init__(self, x=x):409>         self.x = x410>     def add(self, element):411>         self.x += element412>413> assert D().x is D().x414> ```415>416> Это имеет ту же проблему, что и исходный пример с классом `C`. А именно, два экземпляра класса `D`, которые не указывают значение для `x` при создании экземпляра класса, будут совместно использовать одну копию `x`. Поскольку dataclasses используют обычное создание классов Python, они также разделяют это поведение. Не существует общего способа для Data Classes обнаружить это условие. Вместо этого dataclasses вызовут [`TypeError`](https://python-all.ru/3.9/library/exceptions.html#TypeError), если обнаружат параметр по умолчанию типа `list`, `dict` или `set`. Это частичное решение, но оно защищает от многих распространённых ошибок.417>418> Использование фабричных функций по умолчанию – это способ создавать новые экземпляры изменяемых типов в качестве значений полей по умолчанию:419>420> ```python421> @dataclass422> class D:423>     x: list = field(default_factory=list)424>425> assert D().x is not D().x426> ```427428## Исключения429430#### `exception dataclasses.FrozenInstanceError`431432Возбуждается, когда неявно определённые [`__setattr__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__setattr__) или [`__delattr__()`](https://python-all.ru/3.9/reference/datamodel.html#object.__delattr__) вызываются для dataclass, определённого с `frozen=True`. Является подклассом [`AttributeError`](https://python-all.ru/3.9/library/exceptions.html#AttributeError).433