Создание атрибута класса изменяемого (mutable) типа со значением по-умолчанию с помощью dataclasses.make_dataclass()
Коллеги, изучаю dataclasses и наткнулся на момент, который не могу понять/реализовать.
Общая информация.
В dataclasses невозможно напрямую, без применения объектов самой библиотеки dataclasses создать атрибут класса изменяемого типа. Зачем это сделано описано в доке и еще вот нашел, коллеги подробно обсуждают этот вопрос. Но с помощью методов dataclasses это все же можно сделать.
Вот канонический пример, как делать нельзя:
@dataclass
class Foo:
bar: list = []
Тут случится ValueError
А вот так можно (нужно!):
@dataclass
class Foo:
bar: list = field(default_factory=list)
В этом случае создается атрибут типа List, но значение по-умолчанию не присваивается. Дока не описывает как присвоить дефолтное значение изменяемого типа сразу при определение класса, но коллеги подсказывают:
@dataclass
class Foo:
bar: list = field(default_factory=lambda: [1, 2, 3])
Проблема.
В dataclasses существует метод .make_dataclass(), который определяет датакласс "налету", его не надо заранее определять. Как он работает мне, в общем, тоже понятно, но вот именно с вопросом присвоения дефолтного значения переменной изменяемого типа - затык. По разному пытался, получаю исключения разных типов.
Например:
from dataclasses import make_dataclass, field
Config = make_dataclass('Config', [('x', field(default_factory=lambda: [1,2,3]))])
config = Config()
print(config.x)
На моменте создания экземпляра config = Config() поднимается исключение:
TypeError: Config.__init__() missing 1 required positional argument: 'x'
То есть, атрибут мы создали, но значение не присвоили. Ровно тоже самое происходит если убрать lambda: класс создается без ошибок, атрибут создается, но дефолтное значение игнорируется.
Вопрос
Как создать dataclass с помощью метода .make_dataclass() с атрибутом изменяемого типа, которому сразу присвоится значение по-умолчанию. Возможно ли это в принципе?
Ответы (1 шт):
Вы не указали тип
from dataclasses import make_dataclass, field
import typing
Config = make_dataclass('Config', [('x', typing.Any,field(default_factory=lambda: [1,2,3]))])
config = Config()
print(config.x)
Дополнительно. Здесь приведена реализация def make_dataclass(), где можно увидеть, в частности, как именно разбирается аргумент fields в зависимости от количества значений в каждом кортеже, которых может быть от 1 до 3.
Наблюдение. Вместо typing.Any можно использовать None. Работают также любые типы и константы различных типов :)