python attrs继承的字段值被覆盖



我有一些从基类继承的attrs类。父类有一个字段,该字段也是一个attrs类。

如果我实例化了两个子类的实例并设置了公共继承字段的子字段,那么另一个实例的字段也会被覆盖。

我可以通过使用factory为字段指定partial(方法3)所需的默认值来解决这个问题。

看起来,对于方法1和2,MyField的默认值的实例化只发生一次,并且在子实例(?)之间以某种方式共享(?)

有没有其他方法可以不使用这个hack?

代码如下:

import functools
import attrs

def test_attrs_problem():
@attrs.define
class MyField:
name: str
value: int = 0
@attrs.define
class Parent:
# Test fails with the following two field definitions:
#  --- method 1 ---
# a: MyField = MyField(name="default_name")
#  --- method 2 ---
# a: MyField = attrs.field(default=MyField(name="default_name"))
# Test passes with the following two field definitions:
#  --- method 3 ---
a: MyField = attrs.field(
factory=functools.partial(
MyField,
name="default_name"
)
)
@attrs.define
class Child1(Parent):
b: int = 42
@attrs.define
class Child2(Parent):
c: int = 43
# creating an instance of the Child1 class
c1 = Child1()
c1.a.value = 1
before = c1.a.value
print("before", c1)
# creating a instance of the Child2 class
c2 = Child2()
# setting the common inherited field's value field for the c2 instance
c2.a.value = 2
after = c1.a.value
print("after", c1)
# expecting that the value in the c1 instance is the same
assert after == before

这里发生的是一个经典的特殊情况:

from attrs import define
@define
class C:
a: list = []

除非您使用工厂,否则只有一个MyField实例被C的所有实例及其子类使用。

正如你所发现的,如果你想要每个实例单独的MyField,你必须每次都创建。

我个人认为下面的方法更习惯:

@attrs.define
class MyField:
name: str
value: int = 0
@attrs.define
class Parent:
a: MyField = attrs.Factory(lambda: MyField(name="default_name"))

我找到的一个解决方案是这样的:使用工厂样板文件创建字段的辅助函数:

def my_field(**kwargs):
return attrs.field(factory=lambda: MyField(**kwargs))

,然后使用:

@attrs.define
class Parent:
a: MyField = my_field(...)

最新更新