Python dataclasses.dataclass ссылка на переменную вместо переменной экземпляра - PullRequest
0 голосов
/ 11 июля 2020

Значения по умолчанию в конструкторах для c1 и c2 должны создавать новые переменные экземпляра для b и b. Вместо этого похоже, что c1.a и c2.a ссылаются на одну и ту же переменную. Создает ли @dataclass переменную класса? Это не похоже на предполагаемую функциональность, и я не могу найти ничего о переменных класса в документации. Итак, я думаю, что это ошибка. Может кто-нибудь объяснить мне, как это исправить? Должен ли я сообщать об этом как об ошибке в трекере python?

Я знаю, что эта проблема должна быть связана с тем, как python передает объекты по ссылке и встроенные типы по значению, поскольку атрибут b (который является просто числом с плавающей запятой) показывает ожидаемое / желаемое поведение, в то время как атрибут a (который является определяемым пользователем объектом) является просто ссылкой.

Спасибо!

из классов данных import dataclass

"" "входы" ""

@dataclass
class VS:
    v: float  # value
    s: float  # scale factor
    
    def scaled_value(self):
        return self.v*self.s

@dataclass
class Container:
    a: VS = VS(1, 1)
    b: float = 1

c1 = Container()
c2 = Container()

print(c1)
print(c2)

c1.a.v = -999
c1.b = -999

print(c1)
print(c2)

"" "выходы" ""

Container(a=VS(v=1, s=1), b=1)
Container(a=VS(v=1, s=1), b=1)
Container(a=VS(v=-999, s=1), b=-999)
Container(a=VS(v=-999, s=1), b=1)

1 Ответ

0 голосов
/ 14 июля 2020

Спасибо Эри c S за объяснение:

"" "c1 и c2 используют один и тот же экземпляр a. Это проблема изменяемого аргумента по умолчанию: https://docs.python-guide.org/writing/gotchas/#mutable -default- arguments

Используйте default_factory для создания нового VS для каждого контейнера. "" "

default_factory не позволяет мне иметь уникальный набор значений VS по умолчанию для нескольких атрибутов, поскольку Значения по умолчанию VS должны быть определены в классе данных VS. Например, если я хотел, чтобы a по умолчанию было VS (1,1), но я хотел, чтобы b по умолчанию было VS (1,2), default_factory мне не поможет. Итак, я нашел обходной путь, который заключается в создании словаря ключевых слов и передаче глубокой копии в мой конструктор Container () (обратите внимание, что если я не передаю глубокую копию, у меня возникает та же проблема, что и выше). Вот мой последний фрагмент кода и результат:

"" "Код" ""

@dataclass
class VS:
    v: float = 1 # value
    s: float = 1 # scale factor
    
    def scaled_value(self):
        return self.v*self.s

@dataclass
class Container:
    a: VS = field(default_factory=VS)
    b: float = 1

ip = {'a':VS(2,1),'b':1}
c1 = Container(**deepcopy(ip))
c2 = Container(**deepcopy(ip))

print(c1)
print(c2)

c1.a.v = 0
c1.b = 0

print(c1)
print(c2)

"" "Output" ""

Container (a = VS (v = 2, s = 1), b = 1)

Контейнер (a = VS (v = 2, s = 1), b = 1)

Контейнер (a = VS (v = 0, s = 1), b = 0)

Контейнер (a = VS (v = 2, s = 1), b = 1)

...