《Fluent Python》CH.12_面向对象_继承的优缺点 (菱形继承问题、不靠谱的内置类型的继承、不要使用多重继承)
2021-03-01 01:28
标签:ons 绕过 spl lan 实例化 pos int 多重 org 本章探讨继承和子类化,重点是说明对 Python 而言尤为重要的两个细节: 在 Python 2.2 之前,内置类型(如 list 或 dict)不能子类化。在 Python 2.2 之后,内置类型可以子类化了,但是有个重要的注意事项: 内置类型(使用 C 语言编写)不会调用用户定义的类覆盖的特殊方法。 原生类型的这种行为违背了面向对象编程的一个基本原则:始终应该从 实例(self)所属的类开始搜索方法,即使在超类实现的类中调用也是 如此。在这种糟糕的局面中,__missing__ 方法(参见 3.4.2 节)却能 按预期方式工作,不过这只是特例。 不只实例内部的调用有这个问题(self.get() 不调用 self.getitem()),内置类型的方法调用的其他类的方法,如果 被覆盖了,也不会被调用。 上面的init方法并没有使用我们的setitem()方法 继承自 dict 的 update 方法也不使用我们覆盖的 setitem 方 法:‘three‘ 的值没有重复。 d 是 dict 的实例,使用 ad 中的值更新 d。 dict.update 方法忽略了 AnswerDict.__getitem__ 方法。 直接子类化内置类型(如 dict、list 或 str)容易出错, 因为内置类型的方法通常会忽略用户覆盖的方法。不要子类化内置 类型,用户自己定义的类应该继承 collections 模块 (http://docs.python.org/3/library/collections.html)中的类,例如 UserDict、UserList 和 UserString,这些类做了特殊设计,因 此易于扩展。 这种冲突称为“菱形问题”:python的方法是逐层向上、从左到右依次查找方法来进行解析。 在 C++ 中,程序员必须使用类名限定方法调用来避免这种歧义。 在 Python 3 中,这种方式变得更容易了,如示例 12-4 中 D 类的 pingpong 方法所示。4 然而,有时可能需要绕过方法解析顺序,直接 调用某个超类的方法——这样做有时更方便。例如,D.ping 方法可以 这样写: 《设计模式:可复用面向对象软件的基础》 一书中的适配器模式用的就是多重继承,因此使用多重继承肯定没有错 (那本书中的其他 22 个设计模式都使用单继承,因此多重继承显然不 是灵丹妙药)。 在 Python 标准库中,最常使用多重继承的是 collections.abc 包。 使用多重继承时,一定要明确一开始为什么创建子类。主要原因可 能有: 现代的 Python 中,如果类的作用是定义接口,应该明确把它定义为 抽象基类。Python 3.4 及以上的版本中,我们要创建 abc.ABC 或其 他抽象基类的子类(如果想支持较旧的 Python 版本,参见 11.7.1 节)。 如果一个类的作用是为多个不相关的子类提供方法实现,从而实现 重用,但不体现“是什么”关系,应该把那个类明确地定义为混入类 (mixin class)。从概念上讲,混入不定义新类型,只是打包方 法,便于重用。混入类绝对不能实例化,而且具体类不能只继承混 入类。混入类应该提供某方面的特定行为,只实现少量关系非常紧 密的方法。 具体类可以没有,或最多只有一个具体超类。 如果抽象基类或混入的组合对客户代码非常有用,那就提供一个 类,使用易于理解的方式把它们结合起来。Grady Booch 把这种类 称为聚合类(aggregate class)。 有道理 《Fluent Python》CH.12_面向对象_继承的优缺点 (菱形继承问题、不靠谱的内置类型的继承、不要使用多重继承) 标签:ons 绕过 spl lan 实例化 pos int 多重 org 原文地址:https://www.cnblogs.com/zhazhaacmer/p/14454290.htmlCH.12_面向对象_继承的优缺点
12.1 子类化内置类型很麻烦
基本上,内置类型的方法不会调用子类覆盖的方法。
class DoppelDict(dict):
def __setitem__(self, key, value):
super().__setitem__(key, [value] * 2)
dd = DoppelDict(one=1)
dd
{‘one‘: 1}
dd[‘two‘] = 2
dd
{‘one‘: 1, ‘two‘: [2, 2]}
dd.update(three=3)
dd
{‘one‘: 1, ‘two‘: [2, 2], ‘three‘: 3}
内置类型的方法调用的其他类的方法,如果 被覆盖了,也不会被调用
class AnswerDict(dict):
def __getitem__(self, item):
return 42
ad = AnswerDict(a=‘foo‘)
ad[‘a‘] # 这里调用的子类方法
42
ac = {‘b‘:‘233‘}
ac.update(ad)
ac
{‘b‘: ‘233‘, ‘a‘: ‘foo‘}
如需继承内置类型,进行子类化UserDict、UserList 和 UserString
12.2 多重继承和方法解析顺序
如下:void Derived::display(){
BaseA::show(); //调用BaseA类的show()函数
BaseB::show(); //调用BaseB类的show()函数
cout
def ping(self):
A.ping(self) # 而不是super().ping()
print(‘post-ping:‘, self)
12.3 多重继承的真实应用
使用__mro__ 获得python的父类信息
def print_mro(x):
return x.__mro__
import tkinter
print_mro(tkinter.Toplevel)
(tkinter.Toplevel, tkinter.BaseWidget, tkinter.Misc, tkinter.Wm, object)
12.4 处理多重继承
01. 把接口继承和实现继承区分开
通过 继承重用代码是实现细节,通常可以换用组合和委托模式。而接口继承则是框架的支柱。
02. 使用抽象基类显式表示接口
03. 通过混入重用代码
04. 在名称中明确指明混入
06. 不要子类化多个具体类
07. 为用户提供聚合类
08. “优先使用对象组合,而不是类继承”
上一篇:java标识符和数据类型
下一篇:树状数组
文章标题:《Fluent Python》CH.12_面向对象_继承的优缺点 (菱形继承问题、不靠谱的内置类型的继承、不要使用多重继承)
文章链接:http://soscw.com/index.php/essay/58340.html