dataclasses — Классы данных
Исходный код: Lib/dataclasses.py
Этот модуль предоставляет декоратор и функции для автоматического добавления сгенерированных специальных методов, таких как __init__() и __repr__(), в определяемые пользователем классы. Первоначально он был описан в PEP 557.
Переменные членов, используемые в этих сгенерированных методах, определяются с помощью PEP 526 аннотаций типов. Например, этот код:
from dataclasses import dataclass
@dataclass
class InventoryItem:
"""Class for keeping track of an item in inventory."""
name: str
unit_price: float
quantity_on_hand: int = 0
def total_cost(self) -> float:
return self.unit_price * self.quantity_on_hand
добавит, среди прочего, метод __init__() , который будет выглядеть так:
def __init__(self, name: str, unit_price: float, quantity_on_hand: int = 0):
self.name = name
self.unit_price = unit_price
self.quantity_on_hand = quantity_on_hand
Обратите внимание, что этот метод автоматически добавляется в класс: он не указан напрямую в определении InventoryItem , показанном выше.
Добавлен в версии 3.7.
Содержание модуля
-
@dataclasses.dataclass(*, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False)
-
Данная функция является декоратором, используемым для добавления сгенерированных специальных методов к классам, как описано ниже.
Декоратор
@dataclassпроверяет класс, чтобы найтиfield.fieldопределяется как переменная класса, имеющая аннотацию типа. За исключением двух случаев, описанных ниже, ничего в@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, weakref_slot=False) class C: ...Параметры
@dataclass:-
init: Если истинно (по умолчанию), будет сгенерирован метод
__init__().Если класс уже определяет
__init__(), этот параметр игнорируется. -
repr: Если истинно (по умолчанию), будет сгенерирован метод
__repr__(). Сгенерированная строка repr будет содержать имя класса и имя и repr каждого поля в порядке их определения в классе. Поля, помеченные как исключенные из repr, не включаются. Например:InventoryItem(name='widget', unit_price=3.0, quantity_on_hand=10).Если класс уже определяет
__repr__(), этот параметр игнорируется. -
eq: Если истинно (по умолчанию), будет сгенерирован метод
__eq__(). Этот метод сравнивает класс, как будто он был кортежем его полей в порядке. Оба экземпляра в сравнении должны быть одного и того же типа.Если класс уже определяет
__eq__(), этот параметр игнорируется. -
order: Если истинно (по умолчанию
False), будут сгенерированы методы__lt__(),__le__(),__gt__()и__ge__(). Эти методы сравнивают класс, как если бы он был кортежем его полей в порядке. Оба экземпляра в сравнении должны быть одного и того же типа. Если order истинно, а eq ложно, генерируется исключениеValueError.Если класс уже определяет любой из
__lt__(),__le__(),__gt__(), или__ge__(), генерируется исключениеTypeError. -
unsafe_hash: Если
False(по умолчанию), генерируется метод__hash__()в соответствии с установкой eq и frozen.__hash__()используется встроеннымиhash()и при добавлении объектов в хэшированные коллекции, такие как словари и множества. Наличие__hash__()подразумевает, что экземпляры класса неизменяемы. Изменяемость – сложное свойство, зависящее от намерений программиста, существования и поведения__eq__(), и значений флагов eq и frozen в декораторе@dataclass.По умолчанию
@dataclassне будет неявно добавлять метод__hash__(), если это безопасно. Также он не добавит и не изменит явно определенный метод__hash__(). Установка атрибута класса__hash__ = Noneимеет специфическое значение для Python, как описано в документации__hash__().Если
__hash__()не определён явно или если он равенNone, тогда@dataclass*может* добавить неявно метод__hash__(). Хотя это не рекомендуется, вы можете принудительно заставить@dataclassсоздать метод__hash__()с помощьюunsafe_hash=True. Это может быть необходимо, если ваш класс логически неизменяем, но всё же может быть изменён. Это специализированный случай и его следует тщательно рассмотреть.Вот правила, определяющие неявное создание метода
__hash__(). Обратите внимание, что у вас не может быть явного метода__hash__()в вашем классе dataclass и одновременно установленоunsafe_hash=True; это приведёт к исключениюTypeError.Если eq и frozen оба истинны, по умолчанию
@dataclassсгенерирует метод__hash__(). Если eq истинно, а frozen ложно,__hash__()будет установлено вNone, помечая его как нехешируемый (так как он изменяемый). Если eq ложно,__hash__()останется неизменным, что означает использование метода__hash__()суперкласса (если суперклассobject, это означает обращение к хэшированию на основе id). -
frozen: Если истинно (по умолчанию
False), присвоение полям вызовет исключение. Это имитирует неизменяемые экземпляры. Если__setattr__()или__delattr__()определены в классе, вызывается исключениеTypeError. Смотрите обсуждение ниже. -
match_args: Если истинно (по умолчанию
True), кортеж__match_args__будет создан из списка параметров сгенерированного метода__init__()(даже если__init__()не сгенерирован, см. выше). Если ложно, или если__match_args__уже определён в классе,__match_args__не будет сгенерирован.
Добавлена в версии 3.10.
-
kw_only: Если истинно (значение по умолчанию
False), все поля будут помечены как только для ключевых слов. Если поле помечено как только для ключевых слов, единственным следствием является то, что параметр__init__(), сгенерированный из поля только для ключевых слов, должен быть указан с ключевым словом при вызове__init__(). Это не влияет на другие аспекты dataclass. См. запись в глоссарии параметр для получения подробностей. Также см. разделKW_ONLY.
Добавлена в версии 3.10.
-
slots: Если истинно (по умолчанию
False), атрибут__slots__будет сгенерирован, и вместо исходного класса будет возвращён новый. Если__slots__уже определён в классе, генерируется исключениеTypeError. Вызов без аргументовsuper()в dataclasses с использованиемslots=Trueприведёт к возникновению следующего исключения:TypeError: super(type, obj): obj must be an instance or subtype of type. Двухпараметрическийsuper()является допустимым обходным путём. См. gh-90562 для получения подробной информации.
Добавлена в версии 3.10.
Изменено в версии 3.11: Если имя поля уже входит в
__slots__базового класса, оно не будет включено в сгенерированные__slots__для предотвращения их перезаписи. Поэтому не используйте__slots__для получения имён полей dataclass. Используйтеfields()вместо этого. Чтобы иметь возможность определить унаследованные слоты, базовый класс__slots__может быть любым итерируемым объектом, но не итератором.-
weakref_slot: Если истинно (по умолчанию
False), добавляется слот с именем “__weakref__”, необходимый для возможности создания слабоссылочных экземпляров. Указаниеweakref_slot=Trueбез указанияslots=True– ошибка.
Добавлена в версии 3.11.
fieldмогут необязательно указать значение по умолчанию, используя обычный синтаксис Python:@dataclass class C: a: int # 'a' has no default value b: int = 0 # assign a default value for 'b'В этом примере, оба
aиbбудут включены в добавленный метод__init__(), который будет определён как:def __init__(self, a: int, b: int = 0):
-
TypeErrorбудет поднято, если поле без значения по умолчанию следует за полем со значением по умолчанию. Это справедливо, как для одного класса, так и в результате наследования классов.
-
dataclasses.field(*, default=MISSING, default_factory=MISSING, init=True, repr=True, hash=None, compare=True, metadata=None, kw_only=MISSING) -
Для распространённых и простых случаев использования, дополнительной функциональности не требуется. Однако, некоторые возможности Data Classes требуют дополнительной информации для каждого поля. Для удовлетворения этой потребности в дополнительной информации, вы можете заменить значение поля по умолчанию вызовом предоставленной функции
field(). Например:@dataclass class C: mylist: list[int] = field(default_factory=list) c = C() c.mylist += [1, 2, 3]Как показано выше, значение
MISSING— это объект-сентинель, используемый для определения, предоставляются ли некоторые параметры пользователем. Этот сентинель используется потому, чтоNoneявляется допустимым значением для некоторых параметров с отличным значением. Никакой код не должен напрямую использовать значениеMISSING.Параметры для
field():-
default: Если задано, это будет значение по умолчанию для данного поля. Это необходимо, так как вызов
field()сам заменяет обычное положение значения по умолчанию. - default_factory: Если задано, это должен быть вызываемый объект без аргументов, который будет вызван, когда потребуется значение по умолчанию для данного поля. Среди прочего, это можно использовать для указания полей с изменяемыми значениями по умолчанию, как обсуждалось ниже. Запрещается указывать как default, так и default_factory.
-
init: Если True (по умолчанию), это поле включается в качестве параметра в генерируемый метод
__init__(). -
repr: Если True (по умолчанию), это поле включается в строку, возвращаемую генерируемым методом
__repr__(). -
hash: Это может быть bool или
None. Если True, это поле включается в генерируемый метод__hash__(). ЕслиNone(по умолчанию), используется значение compare: это обычно ожидаемое поведение. Поле следует учитывать в хеше, если оно используется для сравнений. Не рекомендуется устанавливать это значение на что-либо, отличное отNone.Одна из возможных причин для установки
hash=False, ноcompare=True— это, если для поля вычисление значения хеша является дорогостоящим, это поле необходимо для проверки равенства, и есть другие поля, которые вносят вклад в хеш типа. Даже если поле исключается из хеша, оно всё равно будет использоваться для сравнений. -
compare: Если True (по умолчанию), это поле включается в генерируемые методы равенства и сравнения (
__eq__(),__gt__()и др.). -
metadata: Это может быть словарь или
None.Noneобрабатывается как пустой словарь. Это значение обернуто вMappingProxyType(), чтобы сделать его только для чтения, и показано в объектеField. Оно совсем не используется Data Classes и предоставляется как механизм расширения для сторонних библиотек. Различные сторонние библиотеки могут иметь свои ключи, чтобы использовать их как пространство имён в метаданных. -
kw_only: Если True, это поле будет помечено как только для ключевых аргументов. Это используется, когда параметры генерируемого метода
__init__()вычисляются.
Добавлен в версии 3.10.
Если значение по умолчанию поля задаётся вызовом
field(), то атрибут класса для этого поля заменяется указанным значением default. Если default не указан, атрибут класса удаляется. Цель состоит в том, что после выполнения декоратора@dataclass, атрибуты класса будут содержать значения по умолчанию для полей, так же, как если бы значение по умолчанию само было указано. Например, после:@dataclass class C: x: int y: int = field(repr=False) z: int = field(repr=False, default=10) t: int = 20Атрибут класса
C.zбудет10, атрибут классаC.tбудет20, а атрибуты классаC.xиC.yне будут установлены. -
default: Если задано, это будет значение по умолчанию для данного поля. Это необходимо, так как вызов
-
class dataclasses.Field -
Объекты
Fieldописывают каждое определённое поле. Эти объекты создаются внутренне и возвращаются методом уровня модуляfields()(см. ниже). Пользователи никогда не должны напрямую создавать объектField. Его документированные атрибуты:-
name: Имя поля. -
type: Тип поля. -
default,default_factory,init,repr,hash,compare,metadata, иkw_onlyимеют идентичное значение и смысл, как и в функцииfield().
Другие атрибуты могут существовать, но они являются закрытыми и не должны проверяться или использоваться.
-
-
dataclasses.fields(class_or_instance) -
Возвращает кортеж объектов
Field, которые определяют поля для этой Data Class. Принимает либо Data Class, либо экземпляр Data Class. ПоднимаетTypeError, если передано не Data Class или экземпляр Data Class. Не возвращает псевдо-поля, которые являютсяClassVarилиInitVar.
-
dataclasses.asdict(obj, *, dict_factory=dict) -
Преобразует Data Class obj в словарь (используя функцию-фабрику dict_factory). Каждая Data Class преобразуется в словарь своих полей, как пары
name: value. Data Classes, словари, списки и кортежи рекурсивно преобразуются. Другие объекты копируются с помощьюcopy.deepcopy().Пример использования
asdict()для вложенных Data Classes:@dataclass class Point: x: int y: int @dataclass class C: mylist: list[Point] p = Point(10, 20) assert asdict(p) == {'x': 10, 'y': 20} c = C([Point(0, 0), Point(10, 4)]) assert asdict(c) == {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}Для создания поверхностной копии может быть использован следующий обходной путь:
{field.name: getattr(obj, field.name) for field in fields(obj)}asdict()поднимаетTypeError, если obj не является экземпляром Data Class.
-
dataclasses.astuple(obj, *, tuple_factory=tuple) -
Преобразует Data Class obj в кортеж (используя функцию-фабрику tuple_factory). Каждая Data Class преобразуется в кортеж значений своих полей. Data Classes, словари, списки и кортежи рекурсивно преобразуются. Другие объекты копируются с помощью
copy.deepcopy().Продолжая предыдущий пример:
assert astuple(p) == (10, 20) assert astuple(c) == ([(0, 0), (10, 4)],)
Для создания поверхностной копии может быть использован следующий обходной путь:
tuple(getattr(obj, field.name) for field in dataclasses.fields(obj))
astuple()поднимаетTypeError, если obj не является экземпляром Data Class.
-
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) -
Создаёт новую Data Class с именем cls_name, полями, определёнными в fields, базовыми классами, заданными в bases, и инициализируется пространством имён, заданным в namespace. fields — это итерируемый объект, элементы которого — это либо
name, либо(name, type), либо(name, type, Field). Если задан толькоname, то дляtypeиспользуетсяtyping.Any. Значения init, repr, eq, order, unsafe_hash, frozen, match_args, kw_only, slots и weakref_slot имеют тот же смысл, что и в@dataclass.Если module определён, атрибут
__module__Data Class устанавливается в это значение. По умолчанию он устанавливается в имя модуля вызывающего кода.Эта функция не строго необходима, так как любой механизм Python для создания нового класса с
__annotations__может применить функцию@dataclassдля преобразования этого класса в Data Class. Эта функция предоставлена для удобства. Например:C = make_dataclass('C', [('x', int), 'y', ('z', int, field(default=5))], namespace={'add_one': lambda self: self.x + 1})Эквивалентно:
@dataclass class C: x: int y: 'typing.Any' z: int = 5 def add_one(self): return self.x + 1
-
dataclasses.replace(obj, /, **changes) -
Создаёт новый объект того же типа, что и obj, заменяя поля значениями из changes. Если obj не является классом данных, генерирует
TypeError. Если ключи в changes не являются именами полей данного класса данных, генерируетTypeError.Новый возвращаемый объект создаётся путём вызова метода
__init__()класса данных. Это гарантирует, что__post_init__(), если он определён, также будет вызван.Если существуют поля только для инициализации без значений по умолчанию, они должны быть указаны при вызове
replace(), чтобы они могли быть переданы в__init__()и__post_init__().Ошибка возникнет, если changes содержит поля, определённые как поля только для инициализации. В этом случае будет выброшено исключение
ValueError.Следует учитывать, как работают поля только для инициализации при вызове
replace(). Они не копируются из исходного объекта, а инициализируются в__post_init__(), если они вообще инициализируются. Ожидается, что поля только для инициализации будут использоваться редко и осмысленно. Если они используются, рекомендуется использовать альтернативные конструкторы классов или, возможно, пользовательский методreplace()(или с аналогичным названием), который обрабатывает копирование экземпляров.
-
dataclasses.is_dataclass(obj) -
Возвращает
Trueесли его параметр является классом данных (включая подклассы класса данных) или экземпляром одного из них, в противном случае возвращаетFalse.Если вам нужно узнать, является ли класс экземпляром класса данных (а не классом данных самим по себе), добавьте дополнительную проверку на
not isinstance(obj, type):def is_dataclass_instance(obj): return is_dataclass(obj) and not isinstance(obj, type)
-
dataclasses.MISSING -
Значение-маяк, обозначающее отсутствующее значение по умолчанию или функцию по умолчанию.
-
dataclasses.KW_ONLY -
Значение-маяк, используемое в качестве аннотации типа. Любые поля после псевдополя с типом
KW_ONLYпомечаются как поля только для ключевых слов. Обратите внимание, что псевдополе типаKW_ONLYв противном случае полностью игнорируется. Это включает имя такого поля. По соглашению, имя_используется для поляKW_ONLY. Поля только для ключевых слов обозначают параметры__init__(), которые должны быть указаны в качестве ключевых слов при создании экземпляра класса.В этом примере поля
yиzбудут помечены как поля только для ключевых слов:@dataclass class Point: x: float _: KW_ONLY y: float z: float p = Point(0, y=1.5, z=2.0)В одном классе данных запрещено указывать более одного поля с типом
KW_ONLY.Добавлен в версии 3.10.
-
exception dataclasses.FrozenInstanceError -
Выбрасывается, когда неявно определённый метод
__setattr__()или__delattr__()вызывается в классе данных, который был определён сfrozen=True. Это подклассAttributeError.
Обработка после инициализации
-
dataclasses.__post_init__() -
Когда он определён в классе, он вызывается сгенерированным методом
__init__(), обычно какself.__post_init__(). Однако, если определены поляInitVar, они также будут переданы в__post_init__()в порядке их определения в классе. Если метод__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__(), сгенерированный функцией @dataclass, не вызывает методы базового класса __init__(). Если базовый класс имеет метод __init__(), который необходимо вызвать, обычно его вызывают в методе __post_init__():
class Rectangle:
def __init__(self, height, width):
self.height = height
self.width = width
@dataclass
class Square(Rectangle):
side: float
def __post_init__(self):
super().__init__(self.side, self.side)
Однако следует отметить, что в общем случае сгенерированные методами __init__() классом данных не нужно вызывать, поскольку производный класс данных позаботится об инициализации всех полей любого базового класса, являющегося классом данных сам по себе.
См. раздел ниже об инициализируемых только при создании переменных, чтобы узнать, как передавать параметры в __post_init__(). Также см. предупреждение о том, как replace() обрабатывает поля init=False.
Переменные класса
Одно из немногих мест, где @dataclass фактически проверяет тип поля, — это определение, является ли поле переменной класса, как определено в PEP 526. Это делается путём проверки, является ли тип поля типом typing.ClassVar. Если поле является ClassVar, оно исключается из рассмотрения как поле и игнорируется механизмами класса данных. Такие псевдополя ClassVar не возвращаются функцией уровня модуля fields().
Переменные только для инициализации
Ещё одно место, где @dataclass проверяет аннотацию типа, — это определение, является ли поле переменной только для инициализации. Это делается путём проверки, является ли тип поля типом dataclasses.InitVar. Если поле является InitVar, оно считается псевдополем, называемым полем только для инициализации. Поскольку это не настоящее поле, оно не возвращается функцией уровня модуля fields(). Поля только для инициализации добавляются в качестве параметров в сгенерированный метод __init__() и передаются в необязательный метод __post_init__(). В противном случае они не используются классами данных.
Например, предположим, что поле будет инициализироваться из базы данных, если значение не предоставляется при создании класса:
@dataclass
class C:
i: int
j: int | None = None
database: InitVar[DatabaseType | None] = None
def __post_init__(self, database):
if self.j is None and database is not None:
self.j = database.lookup('j')
c = C(10, database=my_database)
В этом случае, fields() вернёт объекты Field для i и j, но не для database.
Замороженные экземпляры
Создать полностью неизменяемые объекты Python невозможно. Однако, передав frozen=True в декоратор @dataclass, можно эмулировать неизменяемость. В этом случае классы данных добавят методы __setattr__() и __delattr__() к классу. Эти методы будут вызывать исключение FrozenInstanceError при вызове.
Использование frozen=True влечёт за собой небольшую потерю производительности: метод __init__() не может использовать простое присваивание для инициализации полей и должен использовать object.__setattr__().
Наследование
Когда класс данных создается с помощью декоратора @dataclass, он просматривает все базовые классы класса в обратном порядке MRO (то есть, начиная с object) и для каждого класса данных добавляет поля из этого базового класса в упорядоченное отображение полей. После добавления всех полей базового класса он добавляет свои собственные поля в упорядоченное отображение. Все сгенерированные методы будут использовать это комбинированное, вычисленное упорядоченное отображение полей. Поскольку поля находятся в порядке добавления, производные классы переопределяют базовые классы. Пример:
@dataclass
class Base:
x: Any = 15.0
y: int = 0
@dataclass
class C(Base):
z: int = 10
x: int = 15
Окончательный список полей в порядке: x, y, z. Окончательный тип x — int, как указано в классе C.
Сгенерированный метод __init__() для C будет выглядеть следующим образом:
def __init__(self, x: int = 15, y: int = 0, z: int = 10):
Переупорядочивание параметров только по ключевому слову в __init__()
После вычисления параметров, необходимых для __init__(), все параметры только по ключевому слову перемещаются после всех обычных (не только по ключевому слову) параметров. Это требование реализации параметров только по ключевому слову в Python: они должны следовать за параметрами, не только по ключевому слову.
В этом примере Base.y, Base.w, и D.t — поля только по ключевому слову, а Base.x и D.z — обычные поля:
@dataclass
class Base:
x: Any = 15.0
_: KW_ONLY
y: int = 0
w: int = 1
@dataclass
class D(Base):
z: int = 10
t: int = field(kw_only=True, default=0)
Сгенерированный метод __init__() для D будет выглядеть следующим образом:
def __init__(self, x: Any = 15.0, z: int = 10, *, y: int = 0, w: int = 1, t: int = 0):
Обратите внимание, что параметры были переупорядочены по сравнению с их порядком в списке полей: параметры, полученные из обычных полей, следуют за параметрами, полученными из полей только по ключевому слову.
Относительный порядок параметров только по ключевому слову сохраняется в переупорядоченном списке параметров __init__().
Функции-фабрики по умолчанию
Если field() задает default_factory, она вызывается без аргументов, когда требуется значение по умолчанию для поля. Например, для создания нового экземпляра списка используйте:
mylist: list = field(default_factory=list)
Если поле исключено из __init__() (используя init=False) и поле также задаёт default_factory, то функция-фабрика по умолчанию всегда будет вызываться из сгенерированной функции __init__() . Это происходит потому, что нет другого способа присвоить полю начальное значение.
Изменяемые значения по умолчанию
Python хранит значения по умолчанию для переменных членов в атрибутах класса. Рассмотрим этот пример, не используя dataclasses:
class C:
x = []
def add(self, element):
self.x.append(element)
o1 = C()
o2 = C()
o1.add(1)
o2.add(2)
assert o1.x == [1, 2]
assert o1.x is o2.x
Обратите внимание, что два экземпляра класса C используют одну и ту же переменную класса x, как ожидалось.
Используя dataclasses, если этот код был допустим:
@dataclass
class D:
x: list = [] # This code raises ValueError
def add(self, element):
self.x.append(element)
это сгенерирует код, похожий на:
class D:
x = []
def __init__(self, x=x):
self.x = x
def add(self, element):
self.x.append(element)
assert D().x is D().x
Это имеет ту же проблему, что и исходный пример с использованием класса C. То есть, два экземпляра класса D, не указывающие значение для x при создании экземпляра класса, будут использовать одну и ту же копию x. Поскольку dataclasses используют обычное создание классов Python, они также демонстрируют это поведение. Нет общего способа для Data Classes определить это условие. Вместо этого декоратор @dataclass сгенерирует исключение ValueError, если обнаружит параметр по умолчанию, не поддерживающий хеширование. Предполагается, что если значение не поддерживает хеширование, оно изменяемо. Это частичное решение, но оно защищает от многих распространённых ошибок.
Использование функций-фабрик по умолчанию — это способ создания новых экземпляров изменяемых типов в качестве значений по умолчанию для полей:
@dataclass
class D:
x: list = field(default_factory=list)
assert D().x is not D().x
Поля с типом дескриптора
Поля, которым присвоено значение объекты дескриптора в качестве значения по умолчанию, имеют следующее специальное поведение:
- Значение для поля, переданное методу
__init__()класса данных, передается методу__set__()дескриптора, а не перезаписывает объект дескриптора. - Аналогично, при получении или установке поля вызывается метод
__get__()или метод__set__()дескриптора, а не возвращение или перезапись объекта дескриптора. - Чтобы определить, содержит ли поле значение по умолчанию,
@dataclassвызовет метод__get__()дескриптора, используя форму доступа к классу:descriptor.__get__(obj=None, type=cls). Если дескриптор в этом случае возвращает значение, оно будет использоваться в качестве значения по умолчанию поля. С другой стороны, если дескриптор в этом случае вызывает исключениеAttributeError, значение по умолчанию для поля не будет предоставлено.
class IntConversionDescriptor:
def __init__(self, *, default):
self._default = default
def __set_name__(self, owner, name):
self._name = "_" + name
def __get__(self, obj, type):
if obj is None:
return self._default
return getattr(obj, self._name, self._default)
def __set__(self, obj, value):
setattr(obj, self._name, int(value))
@dataclass
class InventoryItem:
quantity_on_hand: IntConversionDescriptor = IntConversionDescriptor(default=100)
i = InventoryItem()
print(i.quantity_on_hand) # 100
i.quantity_on_hand = 2.5 # calls __set__ with 2.5
print(i.quantity_on_hand) # 2
Обратите внимание, что если поле анотировано типом дескриптора, но не присвоено объект дескриптора как значение по умолчанию, поле будет работать как обычное поле.
© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/dataclasses.html