Создание атрибута класса изменяемого (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. Работают также любые типы и константы различных типов :)

→ Ссылка