我目前正在尝试 Python 3.7 中引入的新数据类结构。我目前坚持尝试对父类进行一些继承。看起来参数的顺序被我当前的方法搞砸了,以至于子类中的 bool 参数在其他参数之前传递。这会导致类型错误。
from dataclasses import dataclass
@dataclass
class Parent:
name: str
age: int
ugly: bool = False
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f'The Name is {self.name} and {self.name} is {self.age} year old')
@dataclass
class Child(Parent):
school: str
ugly: bool = True
jack = Parent('jack snr', 32, ugly=True)
jack_son = Child('jack jnr', 12, school = 'havard', ugly=True)
jack.print_id()
jack_son.print_id()
当我运行这段代码时,我得到这个 TypeError
:
TypeError: non-default argument 'school' follows default argument
我该如何解决?
原文由 Mysterio 发布,翻译遵循 CC BY-SA 4.0 许可协议
数据类组合属性的方式使您无法在基类中使用具有默认值的属性,然后在子类中使用没有默认值的属性(位置属性)。
这是因为属性是通过从 MRO 的底部开始组合的,并按照先见顺序构建属性的有序列表;覆盖保留在其原始位置。 So
Parent
starts out with['name', 'age', 'ugly']
, whereugly
has a default, and thenChild
adds['school']
to the该列表的末尾(ugly
已经在列表中)。这意味着您最终得到['name', 'age', 'ugly', 'school']
并且因为school
没有默认值,这导致__init__
的参数列表无效这在 继承 下的 PEP-557 Dataclasses 中有记录:
并根据 规格:
您在这里有几个选项可以避免这个问题。
第一个选项是使用单独的基类来强制将具有默认值的字段置于 MRO 顺序中的较晚位置。无论如何,避免直接在要用作基类的类上设置字段,例如
Parent
。以下类层次结构有效:
通过将字段拉出到 单独 的基类中,这些基类具有没有默认值的字段和具有默认值的字段,以及精心选择的继承顺序,您可以生成一个 MRO,将所有没有默认值的字段放在具有默认值的字段之前。 — 的反向 MRO(忽略
object
Child
是:请注意,虽然
Parent
没有设置任何新字段,但它确实继承了_ParentDefaultsBase
的字段,并且 不应 在字段列表顺序中以“最后”结束;上面的命令将_ChildDefaultsBase
最后放置,因此它的字段“获胜”。数据类规则也得到满足; the classes with fields without defaults (_ParentBase
and_ChildBase
) precede the classes with fields with defaults (_ParentDefaultsBase
and_ChildDefaultsBase
).The result is
Parent
andChild
classes with a sane field older, whileChild
is still a subclass ofParent
:所以你可以创建这两个类的实例:
另一种选择是仅使用具有默认值的字段;您仍然可以通过在
__post_init__
中提高一个值来犯错误以不提供school
值:但这 确实 改变了字段顺序;
school
在ugly
之后结束:类型提示检查器 会 抱怨
_no_default
不是字符串。您还可以使用
attrs
项目,该项目启发了dataclasses
。它使用不同的继承合并策略; it pulls overridden fields in a subclass to the end of the fields list, so['name', 'age', 'ugly']
in theParent
class becomes['name', 'age', 'school', 'ugly']
in theChild
班级;通过使用默认值覆盖字段,attrs
允许覆盖而无需执行 MRO 舞蹈。attrs
支持定义没有类型提示的字段,但让我们通过设置坚持 支持的类型提示模式auto_attribs=True
: