Python dataclasses
Теги: python-standart-library
Модуль dataclasses
предоставляет [python-decorator] и функции для автоматического добавления сгенерированных специальных методов, таких как __init__()
и __repr__()
, в пользовательские классы.
@dataclasses.dataclass(*, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False
- декоратор, который используется для добавления сгенерированных специальных методов к классам. Декоратор dataclass()
проверяет класс, чтобы найти поля. Поле определяется как переменная класса, имеющая аннотацию типа. За двумя исключениями, описанными ниже, в dataclass()
ничто не проверяет тип, указанный в аннотации переменной. Порядок полей во всех сгенерированных методах соответствует порядку, в котором они появляются в определении класса. dataclass()
так-же добавит в класс дандер-методы. Если какой-либо из добавленных методов уже существует в классе, поведение зависит от параметра декоратора. Декоратор возвращает тот же класс, для которого он был вызван - новый класс не создается. Если dataclass()
используется просто как простой декоратор без параметров, он действует так, как если бы у него были значения по умолчанию, задокументированные в этой сигнатуре.
Три эквивалентных варианта использования dataclass()
@dataclass
class C:
...
@dataclass()
class C:
...
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False,
frozen=False, match_args=True, kw_only=False, slots=False)
class C:
...
Параметры декоратора устанавливают какие специальные методы должны быть сгенерированы.
Если у класса уже определены __init__()
, __repr__()
, __eq__()
то параметры декоратора будут проигнорированы. В случае других методов ожидается иное поведение, смотри документацию.
Для переменных класса допускается указание дефолтных значений. При этом переменные с дефолтными значениями должны следовать после переменных без дефолта (в обычном порядке), иначе будет поднят TypeError
.
Функция dataclasses.field(*, default=MISSING, default_factory=MISSING, init=True, repr=True, hash=None, compare=True, metadata=None, kw_only=MISSING)
позволяет добавить дополнительную информацию о поле дата-класса.
@dataclass
class C:
mylist: list[int] = field(default_factory=list)
c = C()
c.mylist += [1, 2, 3]
default
: если указано, это будет значение по умолчанию для этого поля. Это необходимо, потому что сам вызовfield()
заменяет нзначения по умолчаниюdefault_factory
: если указано, это должна быть вызываемая функция без аргументов, которая будет вызываться, когда для этого поля требуется значение по умолчанию. Помимо прочего, это можно использовать для указания полей с изменяемыми значениями по умолчанию. Нельзя указывать иdefault
, иdefault_factory
init
: еслиTrue
(по умолчанию), это поле включается в качестве параметра в сгенерированный метод__init__()
repr
: аналогично- см. остальные параметры в доке
Следующие методы реализуют конвертацию в стандартные типы python:
dataclasses.asdict(obj, *, dict_factory=dict)
(по факту это глубокая копия)dataclasses.astuple(obj, *, tuple_factory=tuple)
(аналогично)
Так-как используется глубокая копия, это может быть медленно. Если не нужен рекурсивный обход, дешевле может быть вызвать dataclass_instance.__dict__
. Ссылка на оверфлоу
Предоставлен метод для создания дата-класса (это может быть полезно в каких-то фабриках)
C = make_dataclass('C',
[('x', int),
'y',
('z', int, field(default=5))],
namespace={'add_one': lambda self: self.x + 1})
# equal
@dataclass
class C:
x: int
y: 'typing.Any'
z: int = 5
def add_one(self):
return self.x + 1
После генерации __init__()
в датаклассе вызывается __post_init__()
@dataclass
class C:
a: float
b: float
c: float = field(init=False)
def __post_init__(self):
self.c = self.a + self.b
Сабклассы не вызывают унаследованный __init__()
, поэтому хорошим кейсом будет вызывать __init__()
из родителя через постинит дочернего класса
@dataclass
class Rectangle:
height: float
width: float
@dataclass
class Square(Rectangle):
side: float
def __post_init__(self):
super().__init__(self.side, self.side)
Одним из двух мест, где dataclass()
фактически проверяет тип поля, является определение того, является ли поле переменной класса. Он делает это, проверяя, является ли тип поля typing.ClassVar
. Если поле является ClassVar
, оно исключается из рассмотрения как поле и игнорируется механизмами класса данных. Такие псевдополя ClassVar
не возвращаются функцией fields()
уровня модуля.
Другое место, где dataclass()
проверяет аннотацию типа — это определение того, является ли поле переменной только для инициализации. Он делает это, проверяя, относится ли тип поля к типу dataclasses.InitVar
. Если поле является InitVar
, оно считается псевдополем, называемым полем только для инициализации. Поскольку это не настоящее поле, оно не возвращается функцией fields()
уровня модуля. Поля только для инициализации добавляются в качестве параметров в сгенерированный метод __init__()
и передаются в необязательный метод __post_init__()
. В противном случае они не используются классами данных. Подробнее
Можно инициализировать looks like неизменяемый инстанс датакласса - при попытке доступа через __setattr__()
и __delattr__()
будут подняты ошибки. Подробнее
Стандартная схема наследования python полностью реализована для dataclass
Смотри еще: