热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Python3.7数据类中的类继承

如何解决《Python3.7数据类中的类继承》经验,为你挑选了2个好方法。

我目前正在尝试使用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

我该如何解决?



1> Martijn Piet..:

数据类组合属性的方式使您无法在基类中使用具有默认值的属性,然后在子类中使用没有默认(位置属性)的属性.

那是因为属性是从MRO的底部开始组合的,并按照首先看到的顺序构建属性的有序列表; 覆盖保留在原始位置.所以Parent从一开始['name', 'age', 'ugly'],ugly默认情况下,然后Child添加['school']到该列表的末尾(ugly已经在列表中).这意味着您最终得到['name', 'age', 'ugly', 'school']并且因为school没有默认值,这会导致无效的参数列表__init__.

这在PEP-557数据类中有记录,在继承下:

@dataclass装饰器创建数据类时,它会在反向MRO中查看所有类的基类(即,object从中开始),并且对于它找到的每个数据类,将该基类中的字段添加到有序类中字段映射.添加完所有基类字段后,它会将自己的字段添加到有序映射中.所有生成的方法都将使用这种组合的,计算的有序字段映射.由于字段是按插入顺序排列的,因此派生类会覆盖基类.

规范下:

TypeError如果没有默认值的字段在具有默认值的字段后面,则会引发.当这发生在单个类中时,或者作为类继承的结果时,都是如此.

你有几个选项可以避免这个问题.

第一个选项是使用单独的基类将具有默认值的字段强制到MRO顺序中的稍后位置.不惜一切代价,避免直接在要用作基类的类上设置字段,例如Parent.

以下类层次结构有效:

# base classes with fields; fields without defaults separate from fields with.
@dataclass
class _ParentBase:
    name: str
    age: int

@dataclass
class _ParentDefaultsBase:
    ugly: bool = False

@dataclass
class _ChildBase(_ParentBase):
    school: str

@dataclass
class _ChildDefaultsBase(_ParentDefaultsBase):
    ugly: bool = True

# public classes, deriving from base-with, base-without field classes
# subclasses of public classes should put the public base class up front.

@dataclass
class Parent(_ParentDefaultsBase, _ParentBase):
    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, _ChildDefaultsBase, _ChildBase):
    pass

通过将字段拉出到单独的基类中,其中包含没有默认值的字段和具有默认值的字段,以及精心选择的继承顺序,您可以生成一个MRO,在没有默认值的情况下将所有字段放在默认值之前.反向MRO(忽略object)Child是:

_ParentBase
_ChildBase
_ParentDefaultsBase
_ChildDefaultsBase
Parent

请注意,Parent这不会设置任何新字段,因此它在字段列表顺序中以"last"结尾并不重要.具有无默认值(_ParentBase_ChildBase)的字段的类位于具有默认值(_ParentDefaultsBase_ChildDefaultsBase)的字段的类之前.

其结果是Parent,并Child有一个健全的领域类年纪大了,而Child仍然是一个子类Parent:

>>> from inspect import signature
>>> signature(Parent)
 None>
>>> signature(Child)
 None>
>>> issubclass(Child, Parent)
True

所以你可以创建这两个类的实例:

>>> jack = Parent('jack snr', 32, ugly=True)
>>> jack_son = Child('jack jnr', 12, school='havard', ugly=True)
>>> jack
Parent(name='jack snr', age=32, ugly=True)
>>> jack_son
Child(name='jack jnr', age=12, school='havard', ugly=True)

另一种选择是仅使用具有默认值的字段; 你仍然可以school通过提出一个错误来提供错误__post_init__:

_no_default = object()

@dataclass
class Child(Parent):
    school: str = _no_default
    ugly: bool = True

    def __post_init__(self):
        if self.school is _no_default:
            raise TypeError("__init__ missing 1 required argument: 'school'")

但这确实改变了野外秩序; school结束后ugly:

 None>

并且类型提示检查器抱怨_no_default不是字符串.

您也可以使用attrs项目,这是受到启发的项目dataclasses.它使用不同的继承合并策略; 它拉覆盖在子类中的字段列表的末尾字段,因此['name', 'age', 'ugly']Parent类成为['name', 'age', 'school', 'ugly']Child类; 通过使用默认值覆盖该字段,attrs允许覆盖而无需进行MRO舞蹈.

attrs支持定义没有类型提示的字段,但可以通过设置以下内容来坚持支持的类型提示模式auto_attribs=True:

import attr

@attr.s(auto_attribs=True)
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")

@attr.s(auto_attribs=True)
class Child(Parent):
    school: str
    ugly: bool = True



2> Patrick Haug..:

您看到此错误,因为在具有默认值的参数之后添加了没有默认值的参数.继承字段到数据类的插入顺序与方法解析顺序相反,这意味着Parent字段首先出现,即使它们的子节点稍后写入.

PEP-557的一个例子- 数据类:

@dataclass
class Base:
    x: Any = 15.0
    y: int = 0

@dataclass
class C(Base):
    z: int = 10
    x: int = 15

最终的字段列表按顺序排列x, y, z.最终类型xint,如类中所指定C.

不幸的是,我认为没有办法解决这个问题.我的理解是,如果父类有一个默认参数,那么没有子类可以有非默认参数.


推荐阅读
  • 本文介绍了Python对Excel文件的读取方法,包括模块的安装和使用。通过安装xlrd、xlwt、xlutils、pyExcelerator等模块,可以实现对Excel文件的读取和处理。具体的读取方法包括打开excel文件、抓取所有sheet的名称、定位到指定的表单等。本文提供了两种定位表单的方式,并给出了相应的代码示例。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文介绍了如何使用python从列表中删除所有的零,并将结果以列表形式输出,同时提供了示例格式。 ... [详细]
author-avatar
hedongsheng
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有