摘要:继承的优缺点推出继承的初衷是让新手顺利使用只有专家才能设计出来的框架。多重继承的真实应用多重继承能发挥积极作用。即便是单继承,这个原则也能提升灵活性,因为子类化是一种紧耦合,而且较高的继承树容易倒。
继承的优缺点
推出继承的初衷是让新手顺利使用只有专家才能设计出来的框架。
——Alan Kay
直接子类化内置类型(如 dict、list 或 str)容易出错,因为内置类型的 方法通常会忽略用户覆盖的方法。
不要子类化内置类型,用户自己定义的类应该继承 collections 模块的类,
例如UserDict、UserList 和 UserString,这些类做了特殊设计,因此易于扩展。
import collections class DoppelDict2(collections.UserDict): def __setitem__(self, key, value): super().__setitem__(key, [value] * 2) dd = DoppelDict2(one=1) print(dd) dd["two"] = 2 print(dd) dd.update(three=3) print(dd) class AnswerDict2(collections.UserDict): def __getitem__(self, key): return 42 ad = AnswerDict2(a="foo") print(ad["a"])
综上,本节所述的问题只发生在 C 语言实现的内置类型内部的方法委托上,而且只影响 直接继承内置类型的用户自定义类。
如果子类化使用 Python 编写的类,如 UserDict 或 MutableMapping,就不会受此影响。
多重继承和方法解析顺序class A: def ping(self): print("ping:", self) class B(A): def pong(self): print("pong:", self) class C(A): def pong(self): print("PONG:", self) class D(B, C): def ping(self): super().ping() print("post-ping:", self) def pingpong(self): self.ping() super().ping() self.pong() super().pong() C.pong(self) d = D() d.pong() C.pong(d) #看继承关系 print(D.__mro__)
直接调用 d.pong() 运行的是 B 类中的版本。
Python 能区分 d.pong() 调用的是哪个方法,是因为 Python 会按照特定的顺序遍历继承图。
这个顺序叫方法解析顺序(Method Resolution Order,MRO)。
类都有一个名为__mro__ 的属性,它的值是一个元组,按照方法解析顺序列出各个超类,从当前类一直向上,直到 object 类。D
然而,使用 super() 最安全,也不易过时。调用框架或不受自己控制的类层次结构中的
方法时,尤其适合使用 super()。
1 多重继承能发挥积极作用。
2 《设计模式:可复用面向对象软件的基础》一书中的适配器模式用的就是多重继承,因此使用多重继承肯定没有错
3(那本书中的其他 22 个设计模式都使用单继承,因此多重继承显然不是灵丹妙药)
下面是避免把类图搅乱的一些建议。
01. 把接口继承和实现继承区分开使用多重继承时,一定要明确一开始为什么创建子类。主要原因可能有:
继承接口,创建子类型,实现“是什么”关系
继承实现,通过重用避免代码重复
其实这两条经常同时出现,不过只要可能,一定要明确意图。通过继承重用代码是实
现细节,通常可以换用组合和委托模式。而接口继承则是框架的支柱。
现代的 Python 中,如果类的作用是定义接口,应该明确把它定义为抽象基类。Python
3.4 及以上的版本中,我们要创建 abc.ABC 或其他抽象基类的子类
python没有interface这种定义
03. 通过混入重用代码一个类的作用是为多个不相关的子类提供方法实现
应该把那个类明确地定义为混入类(mixin class)
从概念上讲,混入不定义新类型,只是打包方法,便于重用。
混入类绝对不能实例化,而且具体类不能只继承混入类。
混入类应该提供某方面的特定行为,只实现少量关系非常紧密的方法。
04. 在名称中明确指明混入因为在 Python 中没有把类声明为混入的正规方式,所以强烈推荐在名称中加入...Mixin 后缀。
Tkinter 没有采纳这个建议,如果采纳的话,XView 会变成XViewMixin,Pack 会变成 PackMixin
05. 为用户提供聚合类class Widget(BaseWidget, Pack, Place, Grid): """Internal class. Base class for a widget which can be positioned with the geometry managers Pack, Place or Grid.""" pass
Widget 类的定义体是空的,但是这个类提供了有用的服务:
把四个超类结合在一起,这样需要创建新小组件的用户无需记住全部混入,也不用担心声明 class 语句时有没有遵守特定的顺序。08. “优先使用对象组合,而不是类继承”
这句话引自《设计模式:可复用面向对象软件的基础》一书, 这是我能提供的最佳
建议。
熟悉继承之后,就太容易过度使用它了。出于对秩序的诉求,我们喜欢按整洁
的层次结构放置物品,程序员更是乐此不疲。
即便是单继承,这个原则也能提升灵活性,因为子类化是继承在Django的应用
一种紧耦合,而且较高的继承树容易倒。
page 417 这里有些复杂,等我牛掰了再来看
总结collections.abc 模块中相应的抽象基类
多重继承这把双刃剑。首先,我们说明了 mro 类属性中蕴藏的方法解析顺序,有了这一机制,继承方法的名称不再会发生冲突
不要子类化内置类型,用户自己定义的类应该继承 collections 模块的类
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/44726.html
摘要:例如,的序列协议只需要和两个方法。任何类如,只要使用标准的签名和语义实现了这两个方法,就能用在任何期待序列的地方。方法开放了内置序列实现的棘手逻辑,用于优雅地处理缺失索引和负数索引,以及长度超过目标序列的切片。 序列的修改、散列和切片 接着造Vector2d类 要达到的要求 为了编写Vector(3, 4) 和 Vector(3, 4, 5) 这样的代码,我们可以让 init 法接受任...
摘要:自己定义的抽象基类要继承。抽象基类可以包含具体方法。这里想表达的观点是我们可以偷懒,直接从抽象基类中继承不是那么理想的具体方法。 抽象基类 抽象基类的常见用途: 实现接口时作为超类使用。 然后,说明抽象基类如何检查具体子类是否符合接口定义,以及如何使用注册机制声明一个类实现了某个接口,而不进行子类化操作。 如何让抽象基类自动识别任何符合接口的类——不进行子类化或注册。 接口在动态类...
摘要:但返回的是一个类型的对象,这意味着操作的结果是一个类型的对象。反之,如果对象存在,这次调用就会将其作为函数的输入,并按照与方法的约定返回一个对象。 一、Optional 类入门 Java 8中引入了一个新的类java.util.Optional。变量存在时,Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个空的Optional对象,由方法Optional.empt...
摘要:小总结标准库里的所有映射类型都是利用来实现只有可散列的数据类型才能用作这些映射里的键值不用字典推导用处理找不到的键找不到键返回某种默认值底层是与调用实现的字典插入更新原理其他大多数映射类型都提供了两个很强大的方法和。 字典和集合 标准库里的所有映射类型都是利用 dict 来实现的只有可散列的数据类型才能用作这些映射里的键(值不用) 可散列 一个对象是可散列的 它的散列值是不变的 对象...
摘要:第一章数据类型隐式方法利用快速生成类方法方法通过下标找元素自动支持切片操作可迭代方法与如果是一个自定义类的对象,那么会自己去调用其中由你实现的方法。若返回,则会返回否则返回。一个对象没有函数,解释器会用作为替代。 第一章 python数据类型 1 隐式方法 利用collections.namedtuple 快速生成类 import collections Card = collec...
阅读 2590·2021-11-18 10:02
阅读 2626·2021-11-15 11:38
阅读 3696·2021-11-12 10:36
阅读 694·2021-11-12 10:34
阅读 2887·2021-10-21 09:38
阅读 1477·2021-09-29 09:48
阅读 1491·2021-09-29 09:34
阅读 1088·2021-09-22 10:02