Python继承的优缺点

news2024/11/24 0:43:07

推出继承的初衷是让新手顺利使用只有专家才能设计出来的框架!

子类化内置类型的问题

在Python2.2之前,内置类型不能子类化,如list、dict等。

在Python2.2之后,内置类型可以子类化了,但是要注意的是:内置类型(使用C语言编写)不会调用用户定义的类覆盖特殊方法。

至于内置类型的子类覆盖覆盖的方法会不会隐式调用,CPython没有制定官方规则。基本上,内置类型的方法不会调用子类覆盖的方法。

例如,dict的子类覆盖了__getitem__()方法 但不会被内置类型的get()方法调用,只能应用在[]运算符 dict[key]形式中。(感觉这也是理所当然)

示例,dict的__init__和__upate__方法会忽略掉子类覆盖的__setitem__方法

class DoubleDict(dict):
    def __setitem__(self, key, value):
        super().__setitem__(key, [value] * 2)


dd = DoubleDict(one=1)
print(dd)

dd['two'] = 2
print(dd)

dd.update(three=3)
print(dd)
打印
{'one': 1}
{'one': 1, 'two': [2, 2]}
{'one': 1, 'two': [2, 2], 'three': 3}

知识点:

  1. 字典的init和update方法,都可以使用key=value的形式传入

  1. __setitem__方法,没有影响到init和update方法的功能

  1. super().__setitem__可以委托给超类处理,这里不需要传入self了

原生类型这种行为违背了面向对象编程的一个基本原则:始终应该从实例(self)所属的类开始搜索方法,即使在超类实现的类中调用也是如此。在上面的糟糕的局面中,__missing__方法却能按照预期工作,不过这这是个例。

不过只有实例内部的调用有这个问题,内置类型的方法调用其他类的方法,如果被覆盖了,也不会被调用。

ps:self.get()不会调用self.__getitem__()

示例,dict.update方法会忽略AnswerDict.__getitem__方法

class AnswerDict(dict):
    def __getitem__(self, item):
        return 42


ad = AnswerDict(a=1)
print(ad['a'])

d = {}
d.update(ad)
print(d['a'])

以上可以看到,dict.update方法忽略了AnswerDict.__getitem__()

综上所述:

直接子类化内置类型(list、dict、str等)容易出错,因为内置类型的方法通常会忽略用户覆盖的方法。

不要子类化内置类型,应该继承collection模块中的类,如UserDict、UserList、UserString,这写类都做了特殊设计,因此易于扩展

示例,改为继承UserDict类,上面出现的问题都解决了

import collections


class DoubleDict(collections.UserDict):
    def __setitem__(self, key, value):
        super().__setitem__(key, [value] * 2)


dd = DoubleDict(one=1)
print(dd)

dd['two'] = 2
print(dd)

dd.update(three=3)
print(dd)


class AnswerDict(collections.UserDict):
    def __getitem__(self, item):
        return 42


ad = AnswerDict(a=1)
print(ad['a'])

d = {}
d.update(ad)
print(d['a'])
打印
{'one': [1, 1]}
{'one': [1, 1], 'two': [2, 2]}
{'one': [1, 1], 'two': [2, 2], 'three': [3, 3]}
42
42

多重继承和方法解析顺序-MRO

任何实现多重继承的语言都要处理潜在的命令冲突,这种冲突由不相关的祖先类同名方法引起的,这种冲突称为“菱形问题”

示例,ABCD四个类的继承

class A:
    def ping(self):
        print('ping', self)


class B(A):
    def pong(self):
        print('pong B', self)


class C(A):
    def pong(self):
        print('pong C', self)


class D(B, C):
    def ping(self):
        super().ping()  # supper函通过mro委托给了A类,由A.ping()
        print('post-ping', self)

    def pingpong(self):
        self.ping()  # 调用自己的ping方法
        super().ping()  # 调用超类的ping方法,跳过D类的ping方法,找到A类的ping方法
        self.pong()  # 根据mro顺序,找到B类的pong方法
        super().pong()  # 也根据mro顺序,找到B类的pong方法
        C.pong(self)  # 指定调用C.pong方法,忽略mro顺序,找到C类的pong方法


d = D()
d.pong()

C.pong(d)  # 调用超类中的方法

print(D.__mro__)
打印
pong B <__main__.D object at 0x034461B0>
pong C <__main__.D object at 0x034461B0>
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

知识点:

  1. 超类中的方法都可以直接调用,此时要把示例作为显式参数传入

  1. Python能区分d.pong调用的哪个方法,是因为Python会按照特定的顺序遍历继承图,这个顺序叫做方法解析顺序(Method Resolution Order)。类都有一个叫做__mro__的属性,他的值是一个元组,按照方法解析顺序列出各个超类,从当前类一直向上,知道object类。 比如这里就是D→B→C→A→object。方法继承顺序不仅考虑继承图,还要考虑子类声明中超类的顺序,也就是说改成Class D(C, B),那么D类的__mro__属性就不一样了,变为先搜索C,在搜索B。

  1. 想把方法调用委托给超类,推荐的方式是使用supper()函数。

如果想要绕过方法解析顺序,直接调用超类的方法,可以这样实现

def ping(self):

A.ping(self) # 而不是supper().ping()

print('post-ping', self)

注意,这样直接在类上调用实例方法时,比需显式的传入self,因为这样访问的是未绑定方法(unbound method)

然而,如果不是特殊需要,还是使用supper()最安全。

示例,查看一些类的__mro__属性

>>> bool.__mro__

(<class 'bool'>, <class 'int'>, <class 'object'>)

验证了bool是int的子类

>>> import numbers

>>> numbers.Integral.__mro__

(<class 'numbers.Integral'>, <class 'numbers.Rational'>, <class 'numbers.Real'>, <class 'numbers.Complex'>, <class 'numbers.Number'>, <class 'object'>)

验证了numbers模块的“数字塔”

>>> import io

>>> io.BytesIO.__mro__

(<class '_io.BytesIO'>, <class '_io._BufferedIOBase'>, <class '_io._IOBase'>, <class 'object'>)

io模块中有抽象基类(以Base后缀结尾的是抽象基类)和具体类,

方法解析顺序使用C3算法。 C3算法不用了解,对于开发者来说。

多重继承的真实应用--Tkinter

在Python标准库中,最常使用的多重继承的是collection.abc包。这没什么问题,毕竟连Java都支持接口的多重继承,而抽象基类就是接口说明,只不过是他可以提供具体方法的实现。

ps: Java8也允许提供方法实现,这个新功能在Java中叫默认方法

标准库中,GUI工具包Tkinter把多重继承用到了极致。

下面是Tkinter GUI类层次接口的UML图,使用<<mixin>>标记的类通过多重继承为其他类提供服务。 mixin(混合类型)

  1. Toplevel:表示Tkinter应用程序中顶层窗口的类。

  1. Widget:窗口中所有可见对象的超类。

  1. Button:普通的按钮小组件。

  1. Entry:单行可编辑文本字段。

  1. Text:多行可编辑文本字段。

>>> import tkinter

>>> tkinter.Toplevel.__mro__

(<class 'tkinter.Toplevel'>, <class 'tkinter.BaseWidget'>, <class 'tkinter.Misc'>, <class 'tkinter.Wm'>, <class 'object'>)

>>> tkinter.Text.__mro__

(<class 'tkinter.Text'>, <class 'tkinter.Widget'>, <class 'tkinter.BaseWidget'>, <class 'tkinter.Misc'>, <class 'tkinter.Pack'>, <class 'tkinter.Place'>, <class 'tkinter.Grid'>, <class 'tkinter.XView'>, <class 'tkinter.YView'>, <class 'object'>)

>>>

  • Toplevel是所有图形类中唯一没有继承Widget的,因为他是顶层窗口,行为不像小组件,例如不能依附到窗口或者窗体上。Toplevel继承自Wm,后者提供了直接访问宿主窗口管理器的函数,例如设置窗口标题和窗口边框。

  • Widget直接继承自BaseWidget,还继承了Pack、Place、Grid。后面三个类是几何管理器,负责在窗口或者窗体中排列小组件。各个类封装了不同的布局策略和小组件位置API。

  • Button与多数小组件一样,只是Widget的子代,也间接继承Misc,后者为各个小组件提供了大量方法。

  • Entry是Widget和XView的子类,实现了横向滚动

  • Text是Widget和XView、YView的子类,实现了横向、纵向滚动。

多重继承的8点建议

继承有很多用途,而多重继承增加了可选方案和复杂度。使用多重继承容易得出令人费解和脆弱的设计。

  1. 把接口继承和实现继承区分开

使用多重继承,一定要明确一开始为什么创建子类。主要原因有两个:

  • 继承接口,创建子类型,实现“是什么”关系

  • 继承实现,通过重用避免代码重复

以上两条可能同时出现,但是一定要明确意图。通过继承重用代码是实现细节,通常可以换用组合和委托模式。而接口继承是框架的支柱。

  1. 使用抽象基类显式表示接口

如果类的定义是接口,应该明确把它定义为抽象基类。在Python3.4及以上的版本,抽象基类是通过abc.ABC或者其他抽象基类的继承实现。

  1. 通过混入(mixin)重用代码

如果一个类的作用是是为了多个不相关的子类提供方法,从而实现重用,但不体现“是什么”关系,应该把这个类明确定义为混入类(mixin class)

混入不定义新类型,只是打包方法,便于重用。混入类绝对不能实例化,而且具体类不能只继承混入类。混入类应该提供某方面的特定行为,只实现少量关系非常紧密的方法。

  1. 在名称中明确指明混入

在Python中没有把类声明为混入的正规方式,所以建议在名称中加入Mixin后缀。Tkinter没有采纳这个建议,XView应该变成XviewMixin。

  1. 抽象基类可以作为混入,反过来不成立

抽象基类可以实现具体方法,因此可以作为混入使用。不过抽象基类会定义类型,而混入做不到。抽象基类可以作为其他类的唯一继承,但是混入类不能作为唯一的超类。

抽象基类有个局限是混入没有的,抽象基类中实现的具体方法只能与抽象基类及其超类中的方法协作。

  1. 不要子类化多个具体类

具体类可以没有,或者最多只有一个具体超类。就是说,一个具体类的超类中除了有一个具体超类之外,其他的都是抽象类或混入类。

比如下面的示例,Alpha是具体类,那么Beta和Gamma都必须是抽象基类或者混入:

class MyClass(Alpha, Beta, Gamma):

"""一个具体类"""

  1. 为用户提供聚合类

如果抽象基类或者混入的组合对客户代码非常有用,那就提供一个类,使用易于理解的方式把他们结合起来,这种类称为聚合类(aggregate class).

这种类的定义体是空的,重点是把多个超类结合起来,这样用户只需要继承结合后的一个超类。

比如Tkinter中的Widget类就是结合了四个超类。

  1. 优先使用对象组合,而不是类继承

优先使用组合能让设计更加灵活,组合和委托可以代替混入,把行为提供给不同的类,但是不能取代接口继承去定义类型层次结构。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/189154.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Individual tree segmentation and tree-counting using supervised clustering

ABSTRACT 个体树木分割 (ITS) 或树木计数是精准林业和农业过程中的一项基础工作。与费时费力的人工检查不同&#xff0c;计算机视觉在基于无人机 (UAV) 的应用中显示出巨大的前景&#xff1b;此类应用之一包括森林资源清单中的自动树木计数问题。然而&#xff0c;由于树冠冠层…

深度学习性能评估指标介绍

首先是相关数据描述。假设原始样本中有两类数据&#xff0c;其中&#xff1a;总共有P个类别为1的样本&#xff0c;假设类别1为正例总共有N个类别为0的样本&#xff0c;假设类别0为负例经过分类后&#xff1a;有TP个类别为1的样本被系统正确判定为类别1&#xff0c;FN个类别为1的…

【哈希表】leetcode1. 两数之和(C/C++/Java/Python/Js)

leetcode1. 两数之和1 题目2 思路3 代码3.1 C版本3.2 C版本3.3 Java版本3.4 Python版本3.5 JavaScript版本4 总结1 题目 题源链接 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数…

iPad 屏幕镜像到 macbook

将iPad 到屏幕投屏到 macbook&#xff0c;只需要三步就可以实现用数据线连接ipad和macbook在macbook的应用中找到QuickTime Player&#xff0c;打开QuickTime Player&#xff0c;在【文件】中选择【新建影片】在弹出窗口的小箭头中&#xff0c;选择需要的iPad名称通过数据线连接…

目标跟踪心得篇七:解决目标跟踪评价指标输出为0或异常(Trackeval、MMtracking)

如果在做跟踪任务测评时,发现输出的评价指标全为0或者异常值时该怎么办(如下图)?博主调试了很久发现其实这是MMtracking的一个Bug,因此如果不是用MMtracking框架的话本节可能对你帮助不大。 大致有以下两个内容: TrackEval目前还不能做到对多类别的MOT任务计算评价指标,…

FDD与TDD

TDD&#xff0c;时分双工(Time Division Duplexing) FDD&#xff0c;频分双工(Frequency Division Duplexing) 帮助理解&#xff1a; 1.FDD&#xff1a;双车道&#xff0c;一个车道只能走一个方向&#xff0c;双向互不干扰。 2.TDD&#xff1a;单车道&#xff0c;不同时间允…

RabbitMQ看这一篇文章就够了

第一章 RabbitMQ介绍 第1节 MQ是什么 1 2 3 41. 消息队列(Message Queue),又叫做消息中间件 2. 用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成 3. 通过提供消息传递和消息队列模型,可以在分布式环境下扩展进程的通信 4. MQ 是用来解…

北斗短报文遥测终端机在水雨情监测系统中的应用

一、方案概述 我国水利监管手段比较单一&#xff0c;水雨情监测移动公网覆盖不足等诸多问题&#xff0c;利用北斗短报文通信技术数字化信息采集技术&#xff0c;实现水文自动测报&#xff0c;大幅度提升湿地生态和水域的监测、查询、预警和应急处理能力。在恶劣天气情况或特殊灾…

360(drizzleDumper)脱壳教程“某药数据”

一、drizzleDumper的下载使用 1.上GitHub下载开源的脱壳工具mirrors / DrizzleRisk / drizzleDumper GitCodedrizzleDumper是一款基于内存搜索的Android脱壳工具。 &#x1f680; Github 镜像仓库 &#x1f680; 源项目地址 ⬇ ⬇...https://gitcode.net/mirrors/DrizzleRisk…

ZedGraph如何显示鼠标附近的曲线的点?介绍三种方法

使用ZedGraph绘制曲线图的时候&#xff0c;不仅仅是看曲线的走向&#xff0c;也需要查看曲线上某位位置处采集到的数据是多少。下面介绍三种方法&#xff0c;从简单到复杂。 文章目录1、使用自带的功能显示点的坐标2、 多条曲线的坐标点同时显示3、 多条曲线的坐标点同时显示&a…

100%国产C2000,P2P替代TMS320F280049C,独立32位双核CPU,主频高达400MHz

一、特性参数 1、独立双核&#xff0c;32位CPU&#xff0c;单核主频400MHz 2、IEEE 754 单精度浮点单元 &#xff08;FPU&#xff09; 3、三角函数单元 &#xff08;TMU&#xff09; 4、1MB 的 FLASH &#xff08;ECC保护&#xff09; 5、1MB 的 SRAM &#xff08;ECC保护&…

docker基础简介

一、docker架构 二、Docker 基本命令 1、查看 Docker 版本 查看 Docker 版本包括 Docker 版本号、API 版本号、对应的 Git Commit、Containerd 和 runC的版本信息等。 # docker version Client: Docker Engine - Community Version: 20.10.4 API version: 1.40 Go version: …

新手小白做跨境电商应该从哪里入手?

想从事跨境电商先从哪里入手?米贸搜整理如下&#xff0c;希望可以帮助到你跨境电商平台&#xff0c;如&#xff1a;Amazon、eBay、aliexpress、Cdiscount、wish等。想从事跨境电商&#xff0c;你得搭建电商团队&#xff0c;先从跨境电商平台的入驻入手&#xff0c;弄清楚入驻条…

【设计模式】行为型模式·模板方法模式

学习汇总入口【23种设计模式】学习汇总(数万字讲解体系思维导图) 一.概述 定义一个操作中的算法骨架&#xff0c;而将算法的一些步骤延迟到子类中&#xff0c;使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。 在面向对象程序设计过程中&#xff0c;程序员常…

HTB_Inclued_TFTP文件包含与LXD提权

文章目录信息收集TFTPLXD 提权环境安装报错信息收集 开放80端口&#xff0c;url为http://ip:port?filehome.php 测试文件包含&#xff0c;本地包含成功&#xff0c;远程失败&#xff0c;尝试上传后门木马反弹shell 根据图示&#xff0c;网站目录为var/www&#xff0c;其他功…

minio 使用docker安装和入门案例demo

minio目录1.安装2.页面web访问3.在界面上传4.使用api上传5.使用api下载目录 公司目前用到文件上传&#xff0c;考虑到费用等情况&#xff0c;可以在公司自己的服务器上搭建一下。本人记录minio的使用情况。 “前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&…

搭建 SpringBoot 项目

创建 SpringBoot 项目 配置application.properties 根据自己数据库进行配置 spring.datasource.nameexamspring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver spring.datasource.usernameroot spring.datasource.passwordroot spring.datasource.urljdbc:mysql://1…

振弦采集模块VMTool配置工具 快速测试功能

振弦采集模块VMTool配置工具 快速测试功能 本章演示在计算机上通过 VMTool 工具读取振弦传感器数据。 假设您的计算机已经有至少一个空闲的 COM 接口。 1 检查 COM 接口名称 在操作系统桌面右键点击“ 我的电脑” &#xff0c; 选择【 属性】&#xff0c;弹出计算机属性对话框&…

X-Bogus流程分析

声明&#xff1a;本文仅限学习交流使用&#xff0c;禁止用于非法用途、商业活动等。否则后果自负。如有侵权&#xff0c;请告知删除&#xff0c;谢谢&#xff01;本教程也没有专门针对某个网站而编写&#xff0c;单纯的技术研究 目录 一、混淆还原 二、插桩还原 三、还原X-B…

关于Vue中Diff算法详解

一、(Diff)是什么? diff 算法是一种通过同层的树节点进行比较的高效算法 1.1. 特点&#xff1a; 比较只会在同层级进行, 不会跨层级比较在diff比较的过程中&#xff0c;循环从两边向中间比较diff 算法的在很多场景下都有应用&#xff0c;在 vue 中&#xff0c;作用于虚拟 dom…