从父类型对象初始化继承数据类的python方式



给定一个数据类结构:

@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

将种子对象字段浅层复制到新的子对象中的正确方法是什么?我不介意甚至将seed转换为Child类型,因为初始化后没有使用父对象。

我为什么要这么做?我想避免改变子类每次RichParent有一个变化,只要父保持一个普通的数据类。

我不确定为什么你想要写一个显式的__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

*

如果你确实需要在Child上自定义init,使用内置的post_init钩子来完成它:

def __post_init__(self):
print(f"{self} lets do stuff here besides the std data init...")

通过讨论浅拷贝,你知道了可变性问题,但是你可以破解一些像self.my_dict = self.my_dict.copy()这样的东西来解决这个问题。

**

据我所知,@dataclass不会将类转换为数据类类型。它只是构建一个__init__和一些方法(使用元类?)。所以ChildNoDC不知道它是数据类风格的类,只是试图将所有传入其构造函数调用的字段传递给Parent__init__Child被"告知"它也是数据分类的,知道得更多。(这也是没有打印child-only字段的原因)。kw-only标志也释放了一些定位和默认限制。

最新更新