[英]A pythonic way to init inherited dataclass from an object of parent type
Given a dataclass structure:给定一个数据类结构:
@dataclass
class RichParent:
many_fields: int = 1
more_fields: bool = False
class Child(RichParent):
some_extra: bool = False
def __init__(seed: RichParent):
# how to init more_fields and more_fields fields here without referencing them directly?
# self.more_fields = seed.more_fields
# self.many_fields = seed.many_fields
pass
What would be the right way to shallow copy seed object fields into the new child object?将种子对象字段浅复制到新的子对象中的正确方法是什么? I wouldn't mind even converting seed to Child type since there is no use for parent object after initialization.我什至不介意将种子转换为子类型,因为初始化后父对象没有用处。
Why do I do that?我为什么要那样做? I want to avoid changing Child class every time RichParent has a change as long as parent stays a plain dataclass.我想避免每次 RichParent 发生更改时更改 Child 类,只要父级保持普通数据类即可。
I am unsure why you'd want to write an explicit __init__
*
, although you may need to be on Python 3.10 to be able to pass in fields specific to Child
to its init.我不确定你为什么要写一个显式的__init__
*
,尽管你可能需要使用Python 3.10才能将特定于Child
的字段传递给它的 init。 **
from dataclasses import dataclass
@dataclass(kw_only=True)
class RichParent:
many_fields: int = 1
more_fields: bool = False
#for some reason not setting `@dataclass`` again here means the built in str/repr
#skips the child-only field.
#also if you don't use `kw_only=True` on Child, can't pass in Child only fields
@dataclass(kw_only=True)
class Child(RichParent):
some_extra: bool = False
seed = RichParent(many_fields=2, more_fields=True)
print(f"\n{seed=}")
child = Child(**vars(seed))
print(f"\n{child=} with {vars(child)=} which does include more_fields")
child2 = Child(**vars(seed),some_extra=True)
print(f"\n{child2=}")
#see the behavior changes in print and field acceptable to constructor
class ChildNoDC(RichParent):
some_extra: bool = False
child_no_dcdec = ChildNoDC(**vars(seed))
print(f"\n{child_no_dcdec=} with {vars(child_no_dcdec)=} which does include more_fields")
try:
child_no_dcdec2 = ChildNoDC(some_extra=True,**vars(seed))
except (Exception,) as e:
print("\n",e, "as expected")
seed=RichParent(many_fields=2, more_fields=True)
child=Child(many_fields=2, more_fields=True, some_extra=False) with vars(child)={'many_fields': 2, 'more_fields': True, 'some_extra': False} which does include more_fields
child2=Child(many_fields=2, more_fields=True, some_extra=True)
child_no_dcdec=ChildNoDC(many_fields=2, more_fields=True) with vars(child_no_dcdec)={'many_fields': 2, 'more_fields': True} which does include more_fields
RichParent.__init__() got an unexpected keyword argument 'some_extra' as expected
*
If you did need some custom init on Child, use the builtin post_init hook to do it:如果您确实需要对 Child 进行一些自定义初始化,请使用内置的 post_init 挂钩来执行此操作:
def __post_init__(self):
print(f"{self} lets do stuff here besides the std data init...")
From talking about shallow copies, you know about mutability issues, but you could hack some stuff like self.my_dict = self.my_dict.copy()
hacks here to work around that.通过谈论浅拷贝,您知道了可变性问题,但是您可以在这里破解一些像self.my_dict = self.my_dict.copy()
这样的东西来解决这个问题。
**
as far as I understand, @dataclass does not flip the class into a dataclass type.据我了解,@dataclass 不会将类翻转为数据类类型。 It just builds an __init__
and some methods, (using metaclasses?).它只是构建一个__init__
和一些方法(使用元类?)。 So ChildNoDC
doesn't know it is dataclass-style class and just tries to pass all fields passed into its constructor call onto Parent
's __init__
.所以ChildNoDC
不知道它是数据类样式的类,只是试图将传递到其构造函数调用中的所有字段传递给Parent
的__init__
。 Child
having been "told" it's dataclassed too, knows better. Child
被“告知”它也是数据分类的,知道得更多。 (That's also the reason the child-only field wasn't being printed). (这也是未打印 child-only 字段的原因)。 And kw-only
flag frees up some positioning and defaults-only constraints too. kw-only
标志也释放了一些定位和默认约束。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.