Rom*_*Zh. 9 python python-3.x python-dataclasses
我想创建数据类现有实例的副本并对其进行修改。
假设我们有一个数据类和该数据类的一个实例:
from dataclasses import dataclass, field, InitVar, replace
@dataclass
class D:
a: float = 10. # Normal attribute with a default value
b: InitVar[float] = 20. # init-only attribute with a default value
c: float = field(init=False) # an attribute that will be defined in __post_init__
def __post_init__(self, b):
self.c = self.a + b
d1 = D()
Run Code Online (Sandbox Code Playgroud)
让我们定义一个实例并尝试制作一个副本(我已经尝试过这篇文章中提出的解决方案):
replace方法:d2 = replace(d1, **{})
Run Code Online (Sandbox Code Playgroud)
抛出错误
InitVar 'b' must be specified with replace()
Run Code Online (Sandbox Code Playgroud)
这似乎是一个已报告的错误,但我不确定是否有任何进展。
__dict__通过从旧对象创建新对象:d2 = D(**d1.__dict__)
Run Code Online (Sandbox Code Playgroud)
抛出错误
__init__() got an unexpected keyword argument 'c'
Run Code Online (Sandbox Code Playgroud)
您对如何正确复制数据类实例或“解决方法”指出的问题有什么建议吗?
self.b中__post_init__)我已经制定了这个似乎有效的解决方法(发布在答案中)。如果有人能发现缺点,我们将不胜感激。
use*_*ica 13
只需使用标准copy模块:
d2 = copy.copy(d1)
Run Code Online (Sandbox Code Playgroud)
如果你想要深拷贝,你可以使用copy.deepcopy.
任何基于调用数据类构造函数的方法都注定会失败。其中包括replace,它委托给构造函数。问题是 InitVar 值没有存储在任何地方,因此无法知道要为 InitVars 传递哪些值。(特别是,self.b不是提供的值- 它是默认值 - 所以你的值被破坏了。)b__post_init__
Rom*_*Zh. -1
我已经做了这个似乎有效的解决方法。如果有人能发现缺点,我们将非常感激。
def copy_dataclass(D_class, d_obj, **kw):
input = {**kw}
for key, value in asdict(d_obj).items():
# If the attribute is passed to __init__
if d_obj.__dataclass_fields__[key].init:
input[key] = copy.deepcopy(value)
copy_d = D_class(**input)
return copy_d
Run Code Online (Sandbox Code Playgroud)
这使:
d2 = copy_dataclass(D, d1)
d1 == d2
# prints True
d1 is d2
# prints False
Run Code Online (Sandbox Code Playgroud)
如果我们更改 d2 中的字段,它不会影响 d1
d2.a = 50
print(f'd1 = {d1};\nd2 = {d2}')
# d1 = D(a=10.0, c=30.0);
# d2 = D(a=50, c=30.0)
Run Code Online (Sandbox Code Playgroud)
copy.deepcopy(value)用于数据类属性可变的情况;__dict__替换为asdict()方法;InitVars属性的默认值InitVars都已替换(使用D.__dataclass_fields__)。replace()方法......