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

dataclasses.md

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

1> **Источник:** https://python-all.ru/3.10/library/dataclasses.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# [`dataclasses`](https://python-all.ru/3.10/library/dataclasses.html#module-dataclasses) – Классы данных89**Исходный код:** [Lib/dataclasses.py](https://python-all.ru/src/3.10/Lib/dataclasses.py)1011---1213Этот модуль предоставляет декоратор и функции для автоматического добавления генерируемых [специальных методов](https://python-all.ru/3.10/glossary.html#term-special-method), таких как `__init__()` и `__repr__()`, в пользовательские классы. Изначально он описан в [**PEP 557**](https://python-all.ru/3.10/library/dataclasses.html).1415Переменные-члены, используемые в этих сгенерированных методах, определяются с помощью аннотаций типов [**PEP 526**](https://python-all.ru/3.10/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__()`, который будет выглядеть так: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, match_args=True, kw_only=False, slots=False)`4748Эта функция – [декоратор](https://python-all.ru/3.10/glossary.html#term-decorator), который используется для добавления сгенерированных [специальные методы](https://python-all.ru/3.10/glossary.html#term-special-method) в классы, как описано ниже.4950Декоратор [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass) просматривает класс в поисках `field`. `field` определяется как переменная класса, имеющая [аннотацию типа](https://python-all.ru/3.10/glossary.html#term-variable-annotation). За двумя исключениями, описанными ниже, в [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass) тип, указанный в аннотации переменной, не проверяется.5152Порядок полей во всех сгенерированных методах соответствует порядку их объявления в определении класса.5354Декоратор [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass) добавит в класс различные «дандер»-методы, описанные ниже. Если какой-либо из добавляемых методов уже существует в классе, поведение зависит от параметра, как описано ниже. Декоратор возвращает тот же класс, к которому он был применён; новый класс не создаётся.5556Если [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass) используется как простой декоратор без параметров, он ведёт себя так, как если бы использовались значения по умолчанию, описанные в этой сигнатуре. То есть следующие три варианта применения [`dataclass()`](https://python-all.ru/3.10/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,68           match_args=True, kw_only=False, slots=False)69class C:70   ...71```7273Параметры [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass):7475- `init`: Если true (по умолчанию), будет сгенерирован метод `__init__()`.7677  Если класс уже определяет `__init__()`, этот параметр игнорируется.78- `repr`: Если true (по умолчанию), будет сгенерирован метод `__repr__()`. Сгенерированная repr-строка будет содержать имя класса и имя и repr каждого поля в порядке их определения в классе. Поля, помеченные как исключённые из repr, не включаются. Например: `InventoryItem(name='widget', unit_price=3.0, quantity_on_hand=10)`.7980  Если класс уже определяет `__repr__()`, этот параметр игнорируется.81- `eq`: Если true (по умолчанию), будет сгенерирован метод `__eq__()`. Этот метод сравнивает класс так, как если бы он был кортежем его полей, по порядку. Оба сравниваемых экземпляра должны быть одного типа.8283  Если класс уже определяет `__eq__()`, этот параметр игнорируется.84- `order`: Если true (по умолчанию `False`), будут сгенерированы методы `__lt__()`, `__le__()`, `__gt__()` и `__ge__()`. Они сравнивают класс так, как если бы он был кортежем его полей, по порядку. Оба сравниваемых экземпляра должны быть одного типа. Если `order` равно true, а `eq` – false, возбуждается [`ValueError`](https://python-all.ru/3.10/library/exceptions.html#ValueError).8586  Если класс уже определяет любой из `__lt__()`, `__le__()`, `__gt__()` или `__ge__()`, то вызывается [`TypeError`](https://python-all.ru/3.10/library/exceptions.html#TypeError).87- `unsafe_hash`: Если `False` (по умолчанию), метод `__hash__()` генерируется в соответствии с тем, как установлены `eq` и `frozen`.8889  `__hash__()` используется встроенной [`hash()`](https://python-all.ru/3.10/library/functions.html#hash) и при добавлении объектов в хешируемые коллекции, такие как словари и множества. Наличие `__hash__()` подразумевает, что экземпляры класса неизменяемы. Изменяемость – сложное свойство, зависящее от намерений программиста, наличия и поведения `__eq__()`, а также значений флагов `eq` и `frozen` в декораторе [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass).9091  По умолчанию [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass) не будет неявно добавлять метод `__hash__()`, если это не безопасно. Он также не будет добавлять или изменять существующий явно определённый метод `__hash__()`. Установка атрибута класса `__hash__ = None` имеет для Python определённое значение, описанное в документации `__hash__()`.9293  Если `__hash__()` не определён явно или установлен в `None`, то [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass) *может* добавить неявный метод `__hash__()`. Хотя это не рекомендуется, можно принудительно заставить [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass) создать метод `__hash__()` с помощью `unsafe_hash=True`. Это может потребоваться, если класс логически неизменяем, но всё же может быть изменён. Это специализированный случай использования, и к нему следует отнестись с осторожностью.9495  Вот правила, определяющие неявное создание метода `__hash__()`. Обратите внимание, что нельзя одновременно иметь явный метод `__hash__()` в классе данных и устанавливать `unsafe_hash=True`; это приведёт к [`TypeError`](https://python-all.ru/3.10/library/exceptions.html#TypeError).9697  Если `eq` и `frozen` оба равны true, по умолчанию [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass) сгенерирует метод `__hash__()`. Если `eq` равно true, а `frozen` – false, `__hash__()` будет установлено в `None`, что делает объект нехешируемым (каковым он и является, поскольку изменяем). Если `eq` равно false, `__hash__()` останется без изменений, то есть будет использован метод `__hash__()` родительского класса (если родительский класс – [`object`](https://python-all.ru/3.10/library/functions.html#object), это означает возврат к хешированию на основе id).98- `frozen`: Если true (по умолчанию `False`), присваивание полям будет вызывать исключение. Это эмулирует неизменяемые (замороженные) экземпляры. Если `__setattr__()` или `__delattr__()` определён в классе, то возбуждается [`TypeError`](https://python-all.ru/3.10/library/exceptions.html#TypeError). См. обсуждение ниже.99- `match_args`: Если true (по умолчанию `True`), кортеж `__match_args__` будет создан из списка параметров сгенерированного метода `__init__()` (даже если `__init__()` не генерируется, см. выше). Если false, или если `__match_args__` уже определён в классе, то `__match_args__` не будет сгенерирован.100101> Новое в версии 3.10.102103- `kw_only`: Если true (значение по умолчанию `False`), то все поля помечаются как keyword-only. Если поле помечено как keyword-only, то единственный эффект заключается в том, что параметр `__init__()`, сгенерированный из такого поля, должен указываться с ключевым словом при вызове `__init__()`. На остальные аспекты dataclasses это не влияет. Подробнее см. глоссарий, запись [параметр](https://python-all.ru/3.10/glossary.html#term-parameter). Также см. раздел [`KW_ONLY`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.KW_ONLY).104105> Новое в версии 3.10.106107- `slots`: Если true (по умолчанию `False`), будет сгенерирован атрибут `__slots__` и возвращён новый класс вместо исходного. Если `__slots__` уже определён в классе, то возбуждается [`TypeError`](https://python-all.ru/3.10/library/exceptions.html#TypeError).108109> Новое в версии 3.10.110111`field` могут опционально задавать значение по умолчанию, используя обычный синтаксис Python:112113```python114@dataclass115class C:116    a: int       # 'a' не имеет значения по умолчанию117    b: int = 0   # задайте значение по умолчанию для 'b'118```119120В этом примере и `a`, и `b` будут включены в добавленный метод `__init__()`, который будет определён как:121122```python123def __init__(self, a: int, b: int = 0):124```125126[`TypeError`](https://python-all.ru/3.10/library/exceptions.html#TypeError) будет возбуждено, если поле без значения по умолчанию следует за полем со значением по умолчанию. Это верно как для одного класса, так и в случае наследования классов.127128#### `dataclasses.field(*, default=MISSING, default_factory=MISSING, init=True, repr=True, hash=None, compare=True, metadata=None, kw_only=MISSING)`129130Для простых и распространённых случаев никакой дополнительной функциональности не требуется. Однако у dataclass есть некоторые возможности, для которых нужна дополнительная информация о полях. Чтобы восполнить эту потребность, можно заменить значение поля по умолчанию на вызов предоставленной функции [`field()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.field). Например:131132```python133@dataclass134class C:135    mylist: list[int] = field(default_factory=list)136137c = C()138c.mylist += [1, 2, 3]139```140141Как показано выше, значение [`MISSING`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.MISSING) – это объект-страж, используемый для определения того, какие параметры были заданы пользователем. Этот страж используется, потому что `None` является допустимым значением для некоторых параметров и имеет собственный смысл. Код не должен напрямую использовать значение [`MISSING`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.MISSING).142143Параметры [`field()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.field):144145- `default`: Если указано, это будет значением по умолчанию для данного поля. Это необходимо, потому что сам вызов [`field()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.field) заменяет обычную позицию значения по умолчанию.146- `default_factory`: Если указано, это должен быть вызываемый объект без аргументов, который будет вызван, когда потребуется значение по умолчанию для этого поля. Помимо прочего, это можно использовать для указания полей с изменяемыми значениями по умолчанию, как обсуждается ниже. Указывать одновременно `default` и `default_factory` – ошибка.147- `init`: Если true (по умолчанию), это поле включается в качестве параметра в сгенерированный метод `__init__()`.148- `repr`: Если true (по умолчанию), это поле включается в строку, возвращаемую сгенерированным методом `__repr__()`.149- `hash`: Это может быть bool или `None`. Если true, это поле включается в сгенерированный метод `__hash__()`. Если `None` (по умолчанию), используется значение `compare`: обычно это ожидаемое поведение. Поле должно учитываться в хеше, если оно используется для сравнений. Установка этого значения в любое значение, отличное от `None`, не рекомендуется.150151  Одна из возможных причин установить `hash=False`, но `compare=True` – если вычисление хэш-значения для поля дорого, это поле нужно для проверки равенства, а есть другие поля, которые вносят вклад в хэш типа. Даже если поле исключено из хэша, оно всё равно будет использоваться для сравнений.152- `compare`: Если true (по умолчанию), это поле включается в сгенерированные методы равенства и сравнения (`__eq__()`, `__gt__()` и др.).153- `metadata`: Это может быть отображение или None. None обрабатывается как пустой словарь. Это значение оборачивается в [`MappingProxyType()`](https://python-all.ru/3.10/library/types.html#types.MappingProxyType) для обеспечения только для чтения и предоставляется в объекте [`Field`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.Field). Оно вообще не используется Data Classes и предоставляется как механизм расширения сторонними библиотеками. Несколько сторонних библиотек могут иметь свои собственные ключи для использования в качестве пространства имён в метаданных.154- `kw_only`: Если true, это поле будет помечено как keyword-only. Это используется при вычислении параметров сгенерированного метода `__init__()`.155156> Новое в версии 3.10.157158Если значение поля по умолчанию задаётся вызовом [`field()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.field), то атрибут класса для этого поля будет заменён указанным значением `default`. Если `default` не указан, атрибут класса будет удалён. Предполагается, что после выполнения декоратора [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass) атрибуты класса будут содержать значения по умолчанию для полей, как если бы было указано само значение по умолчанию. Например, после:159160```python161@dataclass162class C:163    x: int164    y: int = field(repr=False)165    z: int = field(repr=False, default=10)166    t: int = 20167```168169Атрибут класса `C.z` будет равен `10`, атрибут класса `C.t` будет равен `20`, а атрибуты класса `C.x` и `C.y` не будут установлены.170171#### `class dataclasses.Field`172173Объекты [`Field`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.Field) описывают каждое определённое поле. Эти объекты создаются внутренне и возвращаются методом уровня модуля [`fields()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.fields) (см. ниже). Пользователи никогда не должны создавать экземпляр [`Field`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.Field) напрямую. Его документированные атрибуты:174175> - `name`: Имя поля.176> - `type`: Тип поля.177> - `default`, `default_factory`, `init`, `repr`, `hash`, `compare`, `metadata` и `kw_only` имеют те же значения и смысл, что и в функции [`field()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.field).178179Могут существовать и другие атрибуты, но они приватные, и на них не следует полагаться или проверять их.180181#### `dataclasses.fields(class_or_instance)`182183Возвращает кортеж объектов [`Field`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.Field), определяющих поля этого датакласса. Принимает либо датакласс, либо экземпляр датакласса. Вызывает [`TypeError`](https://python-all.ru/3.10/library/exceptions.html#TypeError), если передан не датакласс и не его экземпляр. Не возвращает псевдополя, которые являются `ClassVar` или `InitVar`.184185#### `dataclasses.asdict(obj, *, dict_factory=dict)`186187Преобразует dataclass `obj` в словарь (с помощью фабричной функции `dict_factory`). Каждый dataclass преобразуется в словарь своих полей в виде пар `name: value`. Рекурсивно обрабатываются dataclass'ы, словари, списки и кортежи. Другие объекты копируются с помощью [`copy.deepcopy()`](https://python-all.ru/3.10/library/copy.html#copy.deepcopy).188189Пример использования [`asdict()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.asdict) на вложенных датаклассах:190191```python192@dataclass193class Point:194     x: int195     y: int196197@dataclass198class C:199     mylist: list[Point]200201p = Point(10, 20)202assert asdict(p) == {'x': 10, 'y': 20}203204c = C([Point(0, 0), Point(10, 4)])205assert asdict(c) == {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}206```207208Для создания поверхностной копии можно использовать следующий обходной путь:209210```python211dict((field.name, getattr(obj, field.name)) for field in fields(obj))212```213214[`asdict()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.asdict) возбуждает [`TypeError`](https://python-all.ru/3.10/library/exceptions.html#TypeError), если `obj` не является экземпляром dataclass.215216#### `dataclasses.astuple(obj, *, tuple_factory=tuple)`217218Преобразует dataclass `obj` в кортеж (с помощью фабричной функции `tuple_factory`). Каждый dataclass преобразуется в кортеж значений своих полей. Рекурсивно обрабатываются dataclass'ы, словари, списки и кортежи. Другие объекты копируются с помощью [`copy.deepcopy()`](https://python-all.ru/3.10/library/copy.html#copy.deepcopy).219220Продолжая предыдущий пример:221222```python223assert astuple(p) == (10, 20)224assert astuple(c) == ([(0, 0), (10, 4)],)225```226227Для создания поверхностной копии можно использовать следующий обходной путь:228229```python230tuple(getattr(obj, field.name) for field in dataclasses.fields(obj))231```232233[`astuple()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.astuple) возбуждает [`TypeError`](https://python-all.ru/3.10/library/exceptions.html#TypeError), если `obj` не является экземпляром dataclass.234235#### `dataclasses.make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False)`236237Создаёт новый dataclass с именем `cls_name`, полями, определёнными в `fields`, базовыми классами, указанными в `bases`, и инициализированным пространством имён из `namespace`. `fields` – это итерируемый объект, элементы которого могут быть `name`, `(name, type)` или `(name, type, Field)`. Если указан только `name`, то `typing.Any` используется для `type`. Значения `init`, `repr`, `eq`, `order`, `unsafe_hash`, `frozen`, `match_args`, `kw_only` и `slots` имеют тот же смысл, что и в [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass).238239Эта функция не является строго обязательной, поскольку любой механизм Python для создания нового класса с помощью `__annotations__` может затем применить функцию [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass) для преобразования этого класса в датакласс. Эта функция предоставляется для удобства. Например:240241```python242C = make_dataclass('C',243                   [('x', int),244                     'y',245                    ('z', int, field(default=5))],246                   namespace={'add_one': lambda self: self.x + 1})247```248249Эквивалентно:250251```python252@dataclass253class C:254    x: int255    y: 'typing.Any'256    z: int = 5257258    def add_one(self):259        return self.x + 1260```261262#### `dataclasses.replace(obj, /, **changes)`263264Создаёт новый объект того же типа, что и `obj`, заменяя поля значениями из `changes`. Если `obj` не является Data Class, возбуждает [`TypeError`](https://python-all.ru/3.10/library/exceptions.html#TypeError). Если значения в `changes` не указывают поля, возбуждает [`TypeError`](https://python-all.ru/3.10/library/exceptions.html#TypeError).265266Новый возвращаемый объект создаётся вызовом метода `__init__()` датакласса. Это гарантирует, что `__post_init__()`, если он присутствует, также будет вызван.267268Переменные, предназначенные только для инициализации, без значений по умолчанию (если таковые имеются) должны быть указаны в вызове [`replace()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.replace), чтобы их можно было передать в `__init__()` и `__post_init__()`.269270Ошибкой считается, если `changes` содержит какие-либо поля, определённые как имеющие `init=False`. В этом случае будет возбуждено [`ValueError`](https://python-all.ru/3.10/library/exceptions.html#ValueError).271272Следует заранее знать, как работают поля `init=False` при вызове [`replace()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.replace). Они не копируются из исходного объекта, а инициализируются в `__post_init__()`, если вообще инициализируются. Ожидается, что поля `init=False` будут использоваться редко и осмотрительно. Если они используются, возможно, разумно иметь альтернативные конструкторы класса или, возможно, пользовательский метод `replace()` (или с похожим именем), который обрабатывает копирование экземпляра.273274#### `dataclasses.is_dataclass(obj)`275276Возвращает `True`, если его параметр является dataclass или экземпляром dataclass, иначе возвращает `False`.277278Если нужно узнать, является ли класс экземпляром dataclass (а не самим dataclass), добавьте дополнительную проверку на `not isinstance(obj, type)`:279280```python281def is_dataclass_instance(obj):282    return is_dataclass(obj) and not isinstance(obj, type)283```284285#### `dataclasses.MISSING`286287Сторожевое значение, обозначающее отсутствие значения по умолчанию или default\_factory.288289#### `dataclasses.KW_ONLY`290291Сторожевое значение, используемое в качестве аннотации типа. Все поля после псевдополя с типом [`KW_ONLY`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.KW_ONLY) помечаются как keyword-only поля. Обратите внимание, что псевдополе типа [`KW_ONLY`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.KW_ONLY) в остальном полностью игнорируется, включая имя такого поля. По соглашению имя `_` используется для поля [`KW_ONLY`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.KW_ONLY). Keyword-only поля означают параметры `__init__()`, которые должны быть указаны как ключевые слова при создании класса.292293В этом примере поля `y` и `z` будут помечены как keyword-only поля:294295```python296@dataclass297class Point:298  x: float299  _: KW_ONLY300  y: float301  z: float302303p = Point(0, y=1.5, z=2.0)304```305306В одном dataclass указывать более одного поля с типом [`KW_ONLY`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.KW_ONLY) – ошибка.307308Новое в версии 3.10.309310#### `exception dataclasses.FrozenInstanceError`311312Возбуждается, когда неявно определённые `__setattr__()` или `__delattr__()` вызываются для dataclass, определённого с `frozen=True`. Является подклассом [`AttributeError`](https://python-all.ru/3.10/library/exceptions.html#AttributeError).313314## Обработка после инициализации315316Сгенерированный код `__init__()` вызовет метод с именем `__post_init__()`, если `__post_init__()` определён в классе. Обычно он вызывается как `self.__post_init__()`. Однако, если определены какие-либо поля `InitVar`, они также будут переданы в `__post_init__()` в порядке их определения в классе. Если метод `__init__()` не сгенерирован, то `__post_init__()` не будет вызываться автоматически.317318Среди прочего, это позволяет инициализировать значения полей, которые зависят от одного или нескольких других полей. Например:319320```python321@dataclass322class C:323    a: float324    b: float325    c: float = field(init=False)326327    def __post_init__(self):328        self.c = self.a + self.b329```330331Метод `__init__()`, сгенерированный [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass), не вызывает методы `__init__()` базового класса. Если базовый класс имеет метод `__init__()`, который необходимо вызвать, обычно этот метод вызывается в методе `__post_init__()`:332333```python334@dataclass335class Rectangle:336    height: float337    width: float338339@dataclass340class Square(Rectangle):341    side: float342343    def __post_init__(self):344        super().__init__(self.side, self.side)345```346347Однако обратите внимание, что в целом вызывать сгенерированные dataclass методы `__init__()` не требуется, поскольку производный dataclass сам позаботится об инициализации всех полей любого базового класса, который сам является dataclass.348349Смотрите раздел ниже о переменных, используемых только для инициализации, чтобы узнать о способах передачи параметров в `__post_init__()`. Также обратите внимание на предупреждение о том, как [`replace()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.replace) обрабатывает поля `init=False`.350351## Переменные класса352353Одно из немногих мест, где [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass) фактически проверяет тип поля, – это определение, является ли поле переменной класса, как определено в [**PEP 526**](https://python-all.ru/3.10/library/dataclasses.html). Это делается путём проверки, является ли тип поля `typing.ClassVar`. Если поле является `ClassVar`, оно исключается из рассмотрения как поле и игнорируется механизмами dataclass. Такие псевдополя `ClassVar` не возвращаются функцией модульного уровня [`fields()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.fields).354355## Переменные только для инициализации356357Ещё одно место, где [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass) проверяет аннотацию типа, – определение, является ли поле переменной, используемой только для инициализации. Для этого проверяется, имеет ли поле тип `dataclasses.InitVar`. Если поле является `InitVar`, оно считается псевдополем, называемым полем, используемым только для инициализации. Поскольку это не настоящее поле, оно не возвращается функцией модульного уровня [`fields()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.fields). Поля, используемые только для инициализации, добавляются как параметры в сгенерированный метод `__init__()` и передаются в необязательный метод `__post_init__()`. В остальном они не используются модулем dataclasses.358359Например, предположим, что поле будет инициализироваться из базы данных, если значение не указано при создании класса:360361```python362@dataclass363class C:364    i: int365    j: int | None = None366    database: InitVar[DatabaseType | None] = None367368    def __post_init__(self, database):369        if self.j is None and database is not None:370            self.j = database.lookup('j')371372c = C(10, database=my_database)373```374375В этом случае [`fields()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.fields) вернёт объекты [`Field`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.Field) для `i` и `j`, но не для `database`.376377## Замороженные экземпляры378379Создать по-настоящему неизменяемые объекты Python невозможно. Однако, передав `frozen=True` декоратору [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass), можно эмулировать неизменяемость. В этом случае dataclasses добавит в класс методы `__setattr__()` и `__delattr__()`. При вызове эти методы вызовут исключение [`FrozenInstanceError`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.FrozenInstanceError).380381Использование `frozen=True` сопряжено с небольшим снижением производительности: `__init__()` не может использовать простое присваивание для инициализации полей и должен использовать [`object.__setattr__()`](https://python-all.ru/3.10/reference/datamodel.html#object.__setattr__).382383## Наследование384385Когда dataclass создаётся декоратором [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass), он просматривает все базовые классы в порядке, обратном MRO (начиная с [`object`](https://python-all.ru/3.10/library/functions.html#object)), и для каждого найденного dataclass добавляет поля из этого базового класса в упорядоченное отображение полей. После добавления всех полей базовых классов он добавляет свои собственные поля в это упорядоченное отображение. Все сгенерированные методы будут использовать это объединённое вычисленное упорядоченное отображение полей. Поскольку поля располагаются в порядке вставки, производные классы переопределяют базовые. Пример:386387```python388@dataclass389class Base:390    x: Any = 15.0391    y: int = 0392393@dataclass394class C(Base):395    z: int = 10396    x: int = 15397```398399Итоговый список полей в порядке: `x`, `y`, `z`. Итоговый тип `x``int`, как указано в классе `C`.400401Сгенерированный метод `__init__()` для `C` будет выглядеть так:402403```python404def __init__(self, x: int = 15, y: int = 0, z: int = 10):405```406407## Переупорядочивание keyword-only параметров в `__init__()`408409После того как вычислены параметры, необходимые для `__init__()`, все параметры, доступные только по ключевому слову, перемещаются так, чтобы они следовали за всеми обычными (не ключевыми) параметрами. Это требование реализации ключевых параметров в Python: они должны идти после обычных параметров.410411В этом примере `Base.y`, `Base.w` и `D.t` – поля, доступные только по ключевому слову, а `Base.x` и `D.z` – обычные поля.412413```python414@dataclass415class Base:416    x: Any = 15.0417    _: KW_ONLY418    y: int = 0419    w: int = 1420421@dataclass422class D(Base):423    z: int = 10424    t: int = field(kw_only=True, default=0)425```426427Сгенерированный метод `__init__()` для `D` будет выглядеть так:428429```python430def __init__(self, x: Any = 15.0, z: int = 10, *, y: int = 0, w: int = 1, t: int = 0):431```432433Обратите внимание, что порядок параметров изменился по сравнению с тем, как они указаны в списке полей: параметры, полученные из обычных полей, следуют за параметрами, полученными из ключевых полей.434435Относительный порядок ключевых параметров сохраняется в переупорядоченном списке параметров `__init__()`.436437## Функции фабрики по умолчанию438439> Если [`field()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.field) указывает `default_factory`, то он вызывается без аргументов, когда требуется значение по умолчанию для поля. Например, чтобы создать новый экземпляр списка, используйте:440>441> ```python442> mylist: list = field(default_factory=list)443> ```444>445> Если поле исключено из `__init__()` (с помощью `init=False`) и поле также указывает `default_factory`, то функция фабрики значений по умолчанию всегда будет вызываться из сгенерированной функции `__init__()`. Это происходит потому, что нет другого способа задать полю начальное значение.446447## Изменяемые значения по умолчанию448449> Python хранит значения переменных-членов по умолчанию в атрибутах класса. Рассмотрим этот пример без использования dataclasses:450>451> ```python452> class C:453>     x = []454>     def add(self, element):455>         self.x.append(element)456>457> o1 = C()458> o2 = C()459> o1.add(1)460> o2.add(2)461> assert o1.x == [1, 2]462> assert o1.x is o2.x463> ```464>465> Обратите внимание, что два экземпляра класса `C` разделяют одну и ту же переменную класса `x`, как и ожидалось.466>467> При использовании dataclasses, *если* бы этот код был корректным:468>469> ```python470> @dataclass471> class D:472>     x: List = []473>     def add(self, element):474>         self.x += element475> ```476>477> он бы сгенерировал код, похожий на:478>479> ```python480> class D:481>     x = []482>     def __init__(self, x=x):483>         self.x = x484>     def add(self, element):485>         self.x += element486>487> assert D().x is D().x488> ```489>490> Это та же проблема, что и в исходном примере с классом `C`. А именно, два экземпляра класса `D`, не указывающие значение для `x` при создании экземпляра класса, будут совместно использовать одну и ту же копию `x`. Поскольку dataclasses используют обычное создание классов Python, они также имеют такое поведение. Не существует общего способа для классов данных обнаружить это условие. Вместо этого декоратор [`dataclass()`](https://python-all.ru/3.10/library/dataclasses.html#dataclasses.dataclass) вызовет исключение [`TypeError`](https://python-all.ru/3.10/library/exceptions.html#TypeError), если обнаружит параметр по умолчанию типа `list`, `dict` или `set`. Это частичное решение, но оно защищает от многих распространённых ошибок.491>492> Использование фабричных функций по умолчанию – это способ создавать новые экземпляры изменяемых типов в качестве значений полей по умолчанию:493>494> ```python495> @dataclass496> class D:497>     x: list = field(default_factory=list)498>499> assert D().x is not D().x500> ```501502## Поля с типом дескриптора503504Поля, которым назначены [объекты-дескрипторы](https://python-all.ru/3.10/reference/datamodel.html#descriptors) в качестве значения по умолчанию, обладают следующим особым поведением:505506- Значение поля, переданное в метод `__init__` класса данных, передается в метод `__set__` дескриптора, а не перезаписывает объект-дескриптор.507- Аналогично, при чтении или записи поля вызывается метод `__get__` или `__set__` дескриптора, а не возвращается или перезаписывается объект-дескриптор.508- Чтобы определить, содержит ли поле значение по умолчанию, `dataclasses` вызывает метод `__get__` дескриптора, используя его форму доступа через класс (т.е. `descriptor.__get__(obj=None, type=cls)`). Если в этом случае дескриптор возвращает значение, оно будет использовано как значение поля по умолчанию. Если же в этой ситуации дескриптор возбуждает исключение [`AttributeError`](https://python-all.ru/3.10/library/exceptions.html#AttributeError), значение по умолчанию для поля предоставлено не будет.509510```python511class IntConversionDescriptor:512  def __init__(self, *, default):513    self._default = default514515  def __set_name__(self, owner, name):516    self._name = "_" + name517518  def __get__(self, obj, type):519    if obj is None:520      return self._default521522    return getattr(obj, self._name, self._default)523524  def __set__(self, obj, value):525    setattr(obj, self._name, int(value))526527@dataclass528class InventoryItem:529  quantity_on_hand: IntConversionDescriptor = IntConversionDescriptor(default=100)530531i = InventoryItem()532print(i.quantity_on_hand)   # 100533i.quantity_on_hand = 2.5    # вызывает __set__ с 2.5534print(i.quantity_on_hand)   # 2535```536537Обратите внимание: если поле аннотировано типом дескриптора, но ему не назначен объект-дескриптор в качестве значения по умолчанию, такое поле будет вести себя как обычное поле.538