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

dataclasses.md

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

1> **Источник:** https://python-all.ru/3.16/library/dataclasses.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# `dataclasses` – Классы данных89**Исходный код:** [Lib/dataclasses.py](https://python-all.ru/src/main/Lib/dataclasses.py)1011---1213Этот модуль предоставляет декоратор и функции для автоматического добавления в пользовательские классы [специальных методов](https://python-all.ru/3.16/glossary.html#term-special-method), таких как [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__) и [`__repr__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__repr__). Изначально он был описан в [**PEP 557**](https://python-all.ru/3.16/library/dataclasses.html).1415Переменные-члены, используемые в этих сгенерированных методах, определяются с помощью аннотаций типов [**PEP 526**](https://python-all.ru/3.16/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, weakref_slot=False)`4748Эта функция – [декоратор](https://python-all.ru/3.16/glossary.html#term-decorator), который используется для добавления в классы сгенерированных [специальных методов](https://python-all.ru/3.16/glossary.html#term-special-method), как описано ниже.4950Декоратор `@dataclass` просматривает класс в поисках `field`. `field` определяется как переменная класса, имеющая [аннотацию типа](https://python-all.ru/3.16/glossary.html#term-variable-annotation). За двумя исключениями, описанными ниже, в `@dataclass` тип, указанный в аннотации переменной, не проверяется.5152Порядок полей во всех сгенерированных методах соответствует порядку их объявления в определении класса.5354Декоратор `@dataclass` добавит в класс различные «дандер»-методы, описанные ниже. Если какой-либо из добавляемых методов уже существует в классе, поведение зависит от параметра, как описано ниже. Декоратор возвращает тот же класс, к которому он был применён; новый класс не создаётся.5556Если `@dataclass` используется как простой декоратор без параметров, он ведёт себя так, как если бы использовались значения по умолчанию, описанные в этой сигнатуре. То есть следующие три варианта применения `@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, weakref_slot=False)69class C:70    ...71```7273Параметры `@dataclass`:7475- *init*: Если true (значение по умолчанию), будет сгенерирован метод [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__).7677  Если класс уже определяет `__init__()`, этот параметр игнорируется.78- *repr*: Если true (значение по умолчанию), будет сгенерирован метод [`__repr__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__repr__). Сгенерированная repr-строка будет содержать имя класса, а также имя и repr каждого поля, в порядке их определения в классе. Поля, помеченные как исключённые из repr, не включаются. Например: `InventoryItem(name='widget', unit_price=3.0, quantity_on_hand=10)`.7980  Если класс уже определяет `__repr__()`, этот параметр игнорируется.81- *eq*: Если true (значение по умолчанию), будет сгенерирован метод [`__eq__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__eq__).8283  Этот метод сравнивает классы путём сравнения каждого поля по порядку. Оба экземпляра в сравнении должны быть одного и того же типа.8485  Если класс уже определяет `__eq__()`, этот параметр игнорируется.8687  Изменено в версии 3.13: Сгенерированный метод `__eq__` теперь сравнивает каждое поле по отдельности (например, `self.a == other.a and self.b == other.b`), а не сравнивает кортежи полей, как в предыдущих версиях.8889  Это изменение ускоряет сравнение, но может изменить результаты в случаях, когда атрибуты сравниваются по идентичности, а не по значению (например, `float('nan')`).9091  В Python 3.12 и более ранних версиях сравнение выполнялось путём создания кортежей полей и их сравнения (например, `(self.a, self.b) == (other.a, other.b)`).92- *order*: Если true (по умолчанию `False`), будут сгенерированы методы [`__lt__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__lt__), [`__le__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__le__), [`__gt__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__gt__) и [`__ge__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__ge__). Они сравнивают класс так, как если бы он был кортежем своих полей, по порядку. Оба экземпляра в сравнении должны быть одного и того же типа. Если *order* установлен в true, а *eq* – в false, вызывается [`ValueError`](https://python-all.ru/3.16/library/exceptions.html#ValueError).9394  Если класс уже определяет любой из `__lt__()`, `__le__()`, `__gt__()` или `__ge__()`, то вызывается [`TypeError`](https://python-all.ru/3.16/library/exceptions.html#TypeError).95- *unsafe\_hash*: If true, force `dataclasses` to create a [`__hash__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__hash__) method, even though it may not be safe to do so. Otherwise, generate a `__hash__()` method according to how *eq* and *frozen* are set. The default value is `False`.9697  `__hash__()` используется встроенной функцией [`hash()`](https://python-all.ru/3.16/library/functions.html#hash), а также когда объекты добавляются в хешируемые коллекции, такие как словари и множества. Наличие `__hash__()` подразумевает, что экземпляры класса неизменяемы. Изменяемость – сложное свойство, зависящее от намерений программиста, наличия и поведения `__eq__()`, а также значений флагов *eq* и *frozen* в декораторе `@dataclass`.9899  По умолчанию `@dataclass` не будет неявно добавлять метод [`__hash__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__hash__), если это не безопасно. Он также не будет добавлять или изменять существующий явно определённый метод `__hash__()`. Установка атрибута класса `__hash__ = None` имеет для Python определённое значение, описанное в документации `__hash__()`.100101  Если `__hash__()` не определён явно или установлен в `None`, то `@dataclass` *может* добавить неявный метод `__hash__()`. Хотя это не рекомендуется, можно принудительно заставить `@dataclass` создать метод `__hash__()` с помощью `unsafe_hash=True`. Это может потребоваться, если класс логически неизменяем, но на практике его всё же можно изменить. Это специализированный случай использования, и к нему следует подходить с осторожностью.102103  Вот правила, определяющие неявное создание метода `__hash__()`. Обратите внимание, что нельзя одновременно иметь явный метод `__hash__()` в классе данных и устанавливать `unsafe_hash=True`; это приведёт к [`TypeError`](https://python-all.ru/3.16/library/exceptions.html#TypeError).104105  Если и *eq*, и *frozen* истинны, по умолчанию `@dataclass` автоматически сгенерирует метод `__hash__()`. Если *eq* истинно, а *frozen* ложно, `__hash__()` будет установлен в `None`, что делает его нехэшируемым (что и так верно, поскольку он изменяем). Если *eq* ложно, `__hash__()` остаётся без изменений, то есть будет использоваться метод `__hash__()` родительского класса (если родительский класс – [`object`](https://python-all.ru/3.16/library/functions.html#object), это означает, что хэширование будет основано на идентификаторах).106- *frozen*: Если истинно (по умолчанию – `False`), присваивание полям будет вызывать исключение. Это имитирует неизменяемые экземпляры. См. [обсуждение](https://python-all.ru/3.16/library/dataclasses.html#dataclasses-frozen) ниже.107108  Если в классе определён [`__setattr__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__setattr__) или [`__delattr__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__delattr__), а *frozen* истинно, то возникает [`TypeError`](https://python-all.ru/3.16/library/exceptions.html#TypeError).109- *match\_args*: Если истинно (по умолчанию – `True`), кортеж [`__match_args__`](https://python-all.ru/3.16/reference/datamodel.html#object.__match_args__) будет создан из списка не-только-ключевых параметров сгенерированного метода [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__) (даже если `__init__()` не создаётся, см. выше). Если ложно или `__match_args__` уже определён в классе, то `__match_args__` генерироваться не будет.110111> Добавлено в версии 3.10.112113- *kw\_only*: Если истинно (по умолчанию – `False`), все поля помечаются как только-ключевые. Если поле помечено как только-ключевое, единственный эффект заключается в том, что параметр [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__), сгенерированный из такого поля, должен быть указан как ключевой при вызове `__init__()`. Подробнее см. глоссарий: [параметр](https://python-all.ru/3.16/glossary.html#term-parameter). Также см. раздел [`KW_ONLY`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.KW_ONLY).114115  Только-ключевые поля не включаются в `__match_args__`.116117> Добавлено в версии 3.10.118119- *slots*: Если истинно (по умолчанию – `False`), будет сгенерирован атрибут [`__slots__`](https://python-all.ru/3.16/reference/datamodel.html#object.__slots__), и вместо исходного класса будет возвращён новый класс. Если `__slots__` уже определён в классе, возникает [`TypeError`](https://python-all.ru/3.16/library/exceptions.html#TypeError).120121> > **Предупреждение**122> >123> > Передача параметров в базовый класс [`__init_subclass__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init_subclass__) при использовании `slots=True` приведёт к [`TypeError`](https://python-all.ru/3.16/library/exceptions.html#TypeError). Либо используйте `__init_subclass__` без параметров, либо укажите значения по умолчанию в качестве обходного пути. Подробнее см. [gh-91126](https://python-all.ru/3.16/library/dataclasses.html).124>125> Добавлено в версии 3.10.126>127> Изменено в версии 3.11: Если имя поля уже присутствует в `__slots__` базового класса, оно не будет включено в сгенерированный `__slots__`, чтобы избежать [их переопределения](https://python-all.ru/3.16/reference/datamodel.html#datamodel-note-slots). Поэтому не используйте `__slots__` для получения имён полей dataclass. Используйте [`fields()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.fields). Чтобы можно было определить унаследованные слоты, `__slots__` базового класса может быть любым итерируемым объектом, но *не* итератором.128129- *weakref\_slot*: Если истинно (по умолчанию – `False`), добавляет слот с именем «\_\_weakref\_\_», который необходим для создания [`weakref-able`](https://python-all.ru/3.16/library/weakref.html#weakref.ref) экземпляра. Указывать `weakref_slot=True` без `slots=True` – ошибка.130131> Добавлено в версии 3.12.132133`field` могут опционально задавать значение по умолчанию, используя обычный синтаксис Python:134135```python136@dataclass137class C:138    a: int       # 'a' не имеет значения по умолчанию139    b: int = 0   # задайте значение по умолчанию для 'b'140```141142В этом примере и `a`, и `b` будут включены в добавленный метод [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__), который будет определён как:143144```python145def __init__(self, a: int, b: int = 0):146```147148[`TypeError`](https://python-all.ru/3.16/library/exceptions.html#TypeError) будет возбуждено, если поле без значения по умолчанию следует за полем со значением по умолчанию. Это верно как для одного класса, так и в случае наследования классов.149150#### `dataclasses.field(*, default=MISSING, default_factory=MISSING, init=True, repr=True, hash=None, compare=True, metadata=None, kw_only=MISSING, doc=None)`151152Для простых и распространённых случаев никакой дополнительной функциональности не требуется. Однако у dataclass есть некоторые возможности, для которых нужна дополнительная информация о полях. Чтобы восполнить эту потребность, можно заменить значение поля по умолчанию на вызов предоставленной функции `field()`. Например:153154```python155@dataclass156class C:157    mylist: list[int] = field(default_factory=list)158159c = C()160c.mylist += [1, 2, 3]161```162163Как показано выше, значение [`MISSING`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.MISSING) – это объект-страж, используемый для определения того, какие параметры были заданы пользователем. Этот страж используется, потому что `None` является допустимым значением для некоторых параметров и имеет собственный смысл. Код не должен напрямую использовать значение `MISSING`.164165Параметры `field()`:166167- *default*: Если указано, это будет значением по умолчанию для данного поля. Это необходимо, поскольку вызов `field()` сам по себе заменяет обычное место значения по умолчанию.168- *default\_factory*: Если указано, это должна быть вызываемая функция без аргументов, которая будет вызвана, когда потребуется значение по умолчанию для этого поля. Среди прочего, это можно использовать для полей с изменяемыми значениями по умолчанию, как описано ниже. Указывать одновременно *default* и *default\_factory* – ошибка.169- *init*: Если истинно (по умолчанию), это поле включается как параметр в сгенерированный метод [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__).170- *repr*: Если истинно (по умолчанию), это поле включается в строку, возвращаемую сгенерированным методом [`__repr__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__repr__).171- *hash*: Может быть bool или `None`. Если истинно, поле включается в сгенерированный метод [`__hash__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__hash__). Если ложно, поле исключается из сгенерированного `__hash__()`. Если `None` (по умолчанию), используется значение *compare*: это обычно ожидаемое поведение, поскольку поле должно включаться в хэш, если оно используется для сравнений. Установка этого значения во что-либо, отличное от `None`, не рекомендуется.172173  Одна из возможных причин установить `hash=False`, но `compare=True` – если вычисление хэш-значения для поля дорого, это поле нужно для проверки равенства, а есть другие поля, которые вносят вклад в хэш типа. Даже если поле исключено из хэша, оно всё равно будет использоваться для сравнений.174- *compare*: Если истинно (по умолчанию), это поле включается в сгенерированные методы равенства и сравнения ([`__eq__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__eq__), [`__gt__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__gt__) и т. д.).175- *metadata*: Может быть отображением (mapping) или `None`. `None` трактуется как пустой словарь. Это значение оборачивается в [`MappingProxyType()`](https://python-all.ru/3.16/library/types.html#types.MappingProxyType) для обеспечения read-only и предоставляется через объект [`Field`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.Field). Data Classes его вообще не используют, это механизм расширения для сторонних библиотек. Разные сторонние разработчики могут использовать свои ключи как пространства имён в метаданных.176- *kw\_only*: Если истинно, это поле помечается как только-ключевое. Используется при вычислении параметров сгенерированного метода [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__).177178  Только-ключевые поля также не включаются в `__match_args__`.179180> Добавлено в версии 3.10.181182- *doc*: необязательная строка документации для этого поля.183184> Добавлено в версии 3.14.185186Если значение по умолчанию для поля задано вызовом `field()`, то атрибут класса для этого поля будет заменён указанным значением *default*. Если *default* не указан, атрибут класса будет удалён. Смысл в том, что после выполнения декоратора [`@dataclass`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.dataclass) все атрибуты класса будут содержать значения по умолчанию для полей, как если бы они были указаны напрямую. Например, после:187188```python189@dataclass190class C:191    x: int192    y: int = field(repr=False)193    z: int = field(repr=False, default=10)194    t: int = 20195```196197Атрибут класса `C.z` будет равен `10`, атрибут класса `C.t` будет равен `20`, а атрибуты класса `C.x` и `C.y` не будут установлены.198199Изменено в версии 3.15: Если *metadata* равно `None`, используется пустой [`frozendict`](https://python-all.ru/3.16/library/stdtypes.html#frozendict), а не [`MappingProxyType()`](https://python-all.ru/3.16/library/types.html#types.MappingProxyType) пустого [`dict`](https://python-all.ru/3.16/library/stdtypes.html#dict).200201#### `class dataclasses.Field`202203Объекты `Field` описывают каждое определённое поле. Эти объекты создаются внутренне и возвращаются методом уровня модуля [`fields()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.fields) (см. ниже). Пользователи никогда не должны создавать экземпляр `Field` напрямую. Его документированные атрибуты:204205- `name`: Имя поля.206- `type`: Тип поля.207- `default`, `default_factory`, `init`, `repr`, `hash`, `compare`, `metadata` и `kw_only` имеют те же значения и смысл, что и в функции [`field()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.field).208209Могут существовать и другие атрибуты, но они приватные, и на них не следует полагаться или проверять их.210211#### `class dataclasses.InitVar`212213Аннотации типа `InitVar[T]` описывают переменные, которые являются [только для инициализации](https://python-all.ru/3.16/library/dataclasses.html#dataclasses-init-only-variables). Поля, аннотированные `InitVar`, считаются псевдополями и, соответственно, не возвращаются функцией [`fields()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.fields) и не используются никак, кроме добавления в качестве параметров в [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__) и необязательного [`__post_init__()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.__post_init__).214215#### `dataclasses.fields(class_or_instance)`216217Возвращает кортеж объектов [`Field`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.Field), определяющих поля этого датакласса. Принимает либо датакласс, либо экземпляр датакласса. Вызывает [`TypeError`](https://python-all.ru/3.16/library/exceptions.html#TypeError), если передан не датакласс и не его экземпляр. Не возвращает псевдополя, которые являются `ClassVar` или `InitVar`.218219#### `dataclasses.asdict(obj, *, dict_factory=dict)`220221Преобразует класс данных *obj* в словарь (с помощью фабричной функции *dict\_factory*). Каждый класс данных преобразуется в словарь своих полей в виде пар `name: value`. Рекурсивно обрабатываются классы данных, словари, замороженные словари (frozendict), списки и кортежи. Остальные объекты копируются с помощью [`copy.deepcopy()`](https://python-all.ru/3.16/library/copy.html#copy.deepcopy).222223Пример использования `asdict()` на вложенных датаклассах:224225```python226@dataclass227class Point:228     x: int229     y: int230231@dataclass232class C:233     mylist: list[Point]234235p = Point(10, 20)236assert asdict(p) == {'x': 10, 'y': 20}237238c = C([Point(0, 0), Point(10, 4)])239assert asdict(c) == {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}240```241242Для создания поверхностной копии можно использовать следующий обходной путь:243244```python245{field.name: getattr(obj, field.name) for field in fields(obj)}246```247248`asdict()` вызывает [`TypeError`](https://python-all.ru/3.16/library/exceptions.html#TypeError), если *obj* не является экземпляром датакласса.249250#### `dataclasses.astuple(obj, *, tuple_factory=tuple)`251252Преобразует класс данных *obj* в кортеж (с помощью фабричной функции *tuple\_factory*). Каждый класс данных преобразуется в кортеж значений своих полей. Рекурсивно обрабатываются классы данных, словари, замороженные словари (frozendict), списки и кортежи. Остальные объекты копируются с помощью [`copy.deepcopy()`](https://python-all.ru/3.16/library/copy.html#copy.deepcopy).253254Продолжая предыдущий пример:255256```python257assert astuple(p) == (10, 20)258assert astuple(c) == ([(0, 0), (10, 4)],)259```260261Для создания поверхностной копии можно использовать следующий обходной путь:262263```python264tuple(getattr(obj, field.name) for field in dataclasses.fields(obj))265```266267`astuple()` вызывает [`TypeError`](https://python-all.ru/3.16/library/exceptions.html#TypeError), если *obj* не является экземпляром датакласса.268269#### `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, weakref_slot=False, module=None, qualname=None, decorator=dataclass)`270271Создаёт новый датакласс с именем *cls\_name*, полями, определёнными в *fields*, базовыми классами из *bases*, и инициализированным пространством имён из *namespace*. *fields* – это итерируемый объект, элементы которого могут быть `name`, `(name, type)` или `(name, type, Field)`. Если указано только `name`, то для `type` используется [`typing.Any`](https://python-all.ru/3.16/library/typing.html#typing.Any). Значения *init*, *repr*, *eq*, *order*, *unsafe\_hash*, *frozen*, *match\_args*, *kw\_only*, *slots* и *weakref\_slot* имеют тот же смысл, что и в [`@dataclass`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.dataclass).272273Если определён *module*, то атрибут `__module__` датакласса устанавливается в это значение. По умолчанию он устанавливается в имя модуля вызывающего кода.274275Если *qualname* определён, атрибут [`__qualname__`](https://python-all.ru/3.16/reference/datamodel.html#type.__qualname__) класса данных устанавливается в это значение. По умолчанию он устанавливается в значение, переданное как *cls\_name*.276277Параметр *decorator* – это вызываемый объект, который будет использоваться для создания датакласса. Он должен принимать объект класса в качестве первого аргумента и те же именованные аргументы, что [`@dataclass`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.dataclass). По умолчанию используется функция `@dataclass`.278279Эта функция не является строго обязательной, поскольку любой механизм Python для создания нового класса с помощью [`__annotations__`](https://python-all.ru/3.16/reference/datamodel.html#object.__annotations__) может затем применить функцию [`@dataclass`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.dataclass) для преобразования этого класса в датакласс. Эта функция предоставляется для удобства. Например:280281```python282C = make_dataclass('C',283                   [('x', int),284                     'y',285                    ('z', int, field(default=5))],286                   namespace={'add_one': lambda self: self.x + 1})287```288289Эквивалентно:290291```python292@dataclass293class C:294    x: int295    y: 'typing.Any'296    z: int = 5297298    def add_one(self):299        return self.x + 1300```301302Добавлено в версии 3.14: Добавлен параметр *decorator*.303304Добавлено в версии 3.16.0a0 (не выпущена): Добавлен параметр *qualname*.305306#### `dataclasses.replace(obj, /, **changes)`307308Создаёт новый объект того же типа, что и *obj*, заменяя поля значениями из *changes*. Если *obj* не является датаклассом, вызывает [`TypeError`](https://python-all.ru/3.16/library/exceptions.html#TypeError). Если ключи в *changes* не являются именами полей данного датакласса, вызывает `TypeError`.309310Новый возвращаемый объект создаётся вызовом метода [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__) датакласса. Это гарантирует, что [`__post_init__()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.__post_init__), если он присутствует, также будет вызван.311312Переменные, предназначенные только для инициализации, без значений по умолчанию (если таковые имеются) должны быть указаны в вызове `replace()`, чтобы их можно было передать в `__init__()` и [`__post_init__()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.__post_init__).313314Ошибкой считается, если *changes* содержит поля, определённые как имеющие `init=False`. В этом случае будет вызвано [`ValueError`](https://python-all.ru/3.16/library/exceptions.html#ValueError).315316Следует заранее знать, как работают поля `init=False` при вызове `replace()`. Они не копируются из исходного объекта, а инициализируются в [`__post_init__()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.__post_init__), если вообще инициализируются. Ожидается, что поля `init=False` будут использоваться редко и осмотрительно. Если они используются, возможно, разумно иметь альтернативные конструкторы класса или, возможно, пользовательский метод `replace()` (или с похожим именем), который обрабатывает копирование экземпляра.317318Экземпляры датаклассов также поддерживаются обобщённой функцией [`copy.replace()`](https://python-all.ru/3.16/library/copy.html#copy.replace).319320#### `dataclasses.is_dataclass(obj)`321322Возвращает `True`, если его параметр является dataclass (включая подклассы dataclass, но не включая [обобщённые псевдонимы](https://python-all.ru/3.16/library/stdtypes.html#types-genericalias)) или экземпляром такого класса, в противном случае возвращает `False`.323324Если нужно узнать, является ли класс экземпляром dataclass (а не самим dataclass), добавьте дополнительную проверку на `not isinstance(obj, type)`:325326```python327def is_dataclass_instance(obj):328    return is_dataclass(obj) and not isinstance(obj, type)329```330331#### `dataclasses.MISSING`332333Сторожевое значение, обозначающее отсутствие значения по умолчанию или default\_factory.334335#### `dataclasses.KW_ONLY`336337Сторожевое значение, используемое в качестве аннотации типа. Все поля после псевдополя с типом `KW_ONLY` помечаются как keyword-only поля. Обратите внимание, что псевдополе типа `KW_ONLY` в остальном полностью игнорируется, включая имя такого поля. По соглашению имя `_` используется для поля `KW_ONLY`. Keyword-only поля означают параметры [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__), которые должны быть указаны как ключевые слова при создании класса.338339В этом примере поля `y` и `z` будут помечены как keyword-only поля:340341```python342@dataclass343class Point:344    x: float345    _: KW_ONLY346    y: float347    z: float348349p = Point(0, y=1.5, z=2.0)350```351352В одном dataclass указывать более одного поля с типом `KW_ONLY` – ошибка.353354Добавлено в версии 3.10.355356#### `exception dataclasses.FrozenInstanceError`357358Возбуждается, когда неявно определённые [`__setattr__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__setattr__) или [`__delattr__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__delattr__) вызываются для dataclass, определённого с `frozen=True`. Является подклассом [`AttributeError`](https://python-all.ru/3.16/library/exceptions.html#AttributeError).359360## Обработка после инициализации361362#### `dataclasses.__post_init__()`363364Если метод определён в классе, он будет вызван сгенерированным [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__), обычно как `self.__post_init__()`. Однако, если определены какие-либо поля `InitVar`, они также будут переданы в `__post_init__()` в порядке их определения в классе. Если метод `__init__()` не сгенерирован, то `__post_init__()` не будет вызван автоматически.365366Среди прочего, это позволяет инициализировать значения полей, которые зависят от одного или нескольких других полей. Например:367368```python369@dataclass370class C:371    a: float372    b: float373    c: float = field(init=False)374375    def __post_init__(self):376        self.c = self.a + self.b377```378379Метод [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__), сгенерированный [`@dataclass`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.dataclass), не вызывает методы `__init__()` базового класса. Если базовый класс имеет метод `__init__()`, который необходимо вызвать, обычно этот метод вызывается в методе [`__post_init__()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.__post_init__):380381```python382class Rectangle:383    def __init__(self, height, width):384        self.height = height385        self.width = width386387@dataclass388class Square(Rectangle):389    side: float390391    def __post_init__(self):392        super().__init__(self.side, self.side)393```394395Однако обратите внимание, что в целом вызывать сгенерированные dataclass методы `__init__()` не требуется, поскольку производный dataclass сам позаботится об инициализации всех полей любого базового класса, который сам является dataclass.396397Смотрите раздел ниже о переменных, используемых только для инициализации, чтобы узнать о способах передачи параметров в `__post_init__()`. Также обратите внимание на предупреждение о том, как [`replace()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.replace) обрабатывает поля `init=False`.398399## Переменные класса400401Одно из немногих мест, где [`@dataclass`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.dataclass) фактически проверяет тип поля, – это определение, является ли поле переменной класса, как определено в [**PEP 526**](https://python-all.ru/3.16/library/dataclasses.html). Это делается путём проверки, является ли тип поля [`typing.ClassVar`](https://python-all.ru/3.16/library/typing.html#typing.ClassVar). Если поле является `ClassVar`, оно исключается из рассмотрения как поле и игнорируется механизмами dataclass. Такие псевдополя `ClassVar` не возвращаются функцией модульного уровня [`fields()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.fields).402403## Переменные только для инициализации404405Ещё одно место, где [`@dataclass`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.dataclass) проверяет аннотацию типа, – определение, является ли поле переменной, используемой только для инициализации. Для этого проверяется, имеет ли поле тип [`InitVar`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.InitVar). Если поле является `InitVar`, оно считается псевдополем, называемым полем, используемым только для инициализации. Поскольку это не настоящее поле, оно не возвращается функцией модульного уровня [`fields()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.fields). Поля, используемые только для инициализации, добавляются как параметры в сгенерированный метод [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__) и передаются в необязательный метод [`__post_init__()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.__post_init__). В остальном они не используются модулем dataclasses.406407Например, предположим, что поле будет инициализироваться из базы данных, если значение не указано при создании класса:408409```python410@dataclass411class C:412    i: int413    j: int | None = None414    database: InitVar[DatabaseType | None] = None415416    def __post_init__(self, database):417        if self.j is None and database is not None:418            self.j = database.lookup('j')419420c = C(10, database=my_database)421```422423В этом случае [`fields()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.fields) вернёт объекты [`Field`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.Field) для `i` и `j`, но не для `database`.424425## Замороженные экземпляры426427Создать по-настоящему неизменяемые объекты Python невозможно. Однако, передав `frozen=True` декоратору [`@dataclass`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.dataclass), можно эмулировать неизменяемость. В этом случае dataclasses добавит в класс методы [`__setattr__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__setattr__) и [`__delattr__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__delattr__). При вызове эти методы вызовут исключение [`FrozenInstanceError`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.FrozenInstanceError).428429Использование `frozen=True` сопряжено с небольшим снижением производительности: [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__) не может использовать простое присваивание для инициализации полей и должен использовать `object.__setattr__()`.430431## Наследование432433Когда dataclass создаётся декоратором [`@dataclass`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.dataclass), он просматривает все базовые классы в порядке, обратном MRO (начиная с [`object`](https://python-all.ru/3.16/library/functions.html#object)), и для каждого найденного dataclass добавляет поля из этого базового класса в упорядоченное отображение полей. После добавления всех полей базовых классов он добавляет свои собственные поля в это упорядоченное отображение. Все сгенерированные методы будут использовать это объединённое вычисленное упорядоченное отображение полей. Поскольку поля располагаются в порядке вставки, производные классы переопределяют базовые. Пример:434435```python436@dataclass437class Base:438    x: Any = 15.0439    y: int = 0440441@dataclass442class C(Base):443    z: int = 10444    x: int = 15445```446447Итоговый список полей в порядке: `x`, `y`, `z`. Итоговый тип `x` – [`int`](https://python-all.ru/3.16/library/functions.html#int), как указано в классе `C`.448449Сгенерированный метод [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__) для `C` будет выглядеть так:450451```python452def __init__(self, x: int = 15, y: int = 0, z: int = 10):453```454455## Переупорядочивание keyword-only параметров в `__init__()`456457После того как вычислены параметры, необходимые для [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__), все параметры, доступные только по ключевому слову, перемещаются так, чтобы они следовали за всеми обычными (не ключевыми) параметрами. Это требование реализации ключевых параметров в Python: они должны идти после обычных параметров.458459В этом примере `Base.y`, `Base.w` и `D.t` – поля, доступные только по ключевому слову, а `Base.x` и `D.z` – обычные поля.460461```python462@dataclass463class Base:464    x: Any = 15.0465    _: KW_ONLY466    y: int = 0467    w: int = 1468469@dataclass470class D(Base):471    z: int = 10472    t: int = field(kw_only=True, default=0)473```474475Сгенерированный метод `__init__()` для `D` будет выглядеть так:476477```python478def __init__(self, x: Any = 15.0, z: int = 10, *, y: int = 0, w: int = 1, t: int = 0):479```480481Обратите внимание, что порядок параметров изменился по сравнению с тем, как они указаны в списке полей: параметры, полученные из обычных полей, следуют за параметрами, полученными из ключевых полей.482483Относительный порядок ключевых параметров сохраняется в переупорядоченном списке параметров `__init__()`.484485## Функции фабрики по умолчанию486487Если [`field()`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.field) указывает *default\_factory*, эта функция вызывается без аргументов, когда требуется значение по умолчанию для поля. Например, чтобы создать новый экземпляр списка, используйте:488489```python490mylist: list = field(default_factory=list)491```492493Если поле исключено из [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__) (с помощью `init=False`) и поле также указывает *default\_factory*, то фабричная функция по умолчанию всегда будет вызываться из сгенерированной функции `__init__()`. Это происходит потому, что нет другого способа задать начальное значение поля.494495## Изменяемые значения по умолчанию496497Python хранит значения переменных-членов по умолчанию в атрибутах класса. Рассмотрим этот пример без использования dataclasses:498499```python500class C:501    x = []502    def add(self, element):503        self.x.append(element)504505o1 = C()506o2 = C()507o1.add(1)508o2.add(2)509assert o1.x == [1, 2]510assert o1.x is o2.x511```512513Обратите внимание, что два экземпляра класса `C` разделяют одну и ту же переменную класса `x`, как и ожидалось.514515При использовании dataclasses, *если* бы этот код был корректным:516517```python518@dataclass519class D:520    x: list = []      # Этот код вызывает ValueError521    def add(self, element):522        self.x.append(element)523```524525он бы сгенерировал код, похожий на:526527```python528class D:529    x = []530    def __init__(self, x=x):531        self.x = x532    def add(self, element):533        self.x.append(element)534535assert D().x is D().x536```537538Это та же проблема, что и в исходном примере с классом `C`. То есть два экземпляра класса `D`, которые не указывают значение для `x` при создании экземпляра класса, будут использовать одну и ту же копию `x`. Поскольку dataclasses просто используют обычное создание классов Python, они также разделяют это поведение. Не существует общего способа для Data Classes обнаружить эту ситуацию. Вместо этого декоратор [`@dataclass`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.dataclass) вызовет исключение [`ValueError`](https://python-all.ru/3.16/library/exceptions.html#ValueError), если обнаружит нехешируемый параметр по умолчанию. Предполагается, что если значение нехешируемо, то оно изменяемо. Это частичное решение, но оно защищает от многих распространённых ошибок.539540Использование фабричных функций по умолчанию – это способ создавать новые экземпляры изменяемых типов в качестве значений полей по умолчанию:541542```python543@dataclass544class D:545    x: list = field(default_factory=list)546547assert D().x is not D().x548```549550Изменено в версии 3.11: Вместо поиска и запрета объектов типа [`list`](https://python-all.ru/3.16/library/stdtypes.html#list), [`dict`](https://python-all.ru/3.16/library/stdtypes.html#dict) или [`set`](https://python-all.ru/3.16/library/stdtypes.html#set), теперь нехешируемые объекты не допускаются в качестве значений по умолчанию. Нехешируемость используется для приблизительной оценки изменяемости.551552## Поля с типом дескриптора553554Поля, которым назначены [объекты-дескрипторы](https://python-all.ru/3.16/reference/datamodel.html#descriptors) в качестве значения по умолчанию, обладают следующим особым поведением:555556- Значение поля, переданное в метод [`__init__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__init__) класса данных, передается в метод [`__set__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__set__) дескриптора, а не перезаписывает объект-дескриптор.557- Аналогично, при чтении или записи поля вызывается метод [`__get__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__get__) или `__set__()` дескриптора, а не возвращается или перезаписывается объект-дескриптор.558- Чтобы определить, содержит ли поле значение по умолчанию, [`@dataclass`](https://python-all.ru/3.16/library/dataclasses.html#dataclasses.dataclass) вызовет метод `__get__()` дескриптора, используя его форму доступа к классу: `descriptor.__get__(obj=None, type=cls)`. Если дескриптор в этом случае возвращает значение, оно будет использовано как значение поля по умолчанию. С другой стороны, если дескриптор вызывает [`AttributeError`](https://python-all.ru/3.16/library/exceptions.html#AttributeError) в этой ситуации, значение по умолчанию для поля предоставлено не будет.559560```python561class IntConversionDescriptor:562    def __init__(self, *, default):563        self._default = default564565    def __set_name__(self, owner, name):566        self._name = "_" + name567568    def __get__(self, obj, type):569        if obj is None:570            return self._default571572        return getattr(obj, self._name, self._default)573574    def __set__(self, obj, value):575        setattr(obj, self._name, int(value))576577@dataclass578class InventoryItem:579    quantity_on_hand: IntConversionDescriptor = IntConversionDescriptor(default=100)580581i = InventoryItem()582print(i.quantity_on_hand)   # 100583i.quantity_on_hand = 2.5    # вызывает __set__ с 2.5584print(i.quantity_on_hand)   # 2585```586587Обратите внимание: если поле аннотировано типом дескриптора, но ему не назначен объект-дескриптор в качестве значения по умолчанию, такое поле будет вести себя как обычное поле.588