Chapter 12 Inheritance - For Good or For Worse
12.1 子类化内置类型
- 内置类型(使用 C 语言编写)中原有的方法不会调用用户定义的类覆盖的特殊方法(直接调用覆盖的方法是可行的,但调用类的其他原有方法则与原来相同。如覆盖
__getitem__不会影响get())。这违背了从实例所属类向上搜索方法的原则; - 自定义的类可以继承
collections模块的UserDict、UserList、UserString等类。
12.2 多重继承和方法解析顺序
“菱形问题”:不相关的祖先类实现了同名方法,导致命名冲突。
- 可以使用类名限定方法调用来避免命名冲突,这样访问的是未绑定方法,需要把实例作为显式参数。如:
d = D()
d.pong() # class D
D.pong(d) # class D
C.pong(d) # class C
- Python 使用方法解析顺序
__mro__类属性来判断调用方法所属的类; super()函数将方法调用委托给超类(遵守 MRO 的顺序寻找),这样最安全。这里通过super()调用父类的函数时不再需要显式传入调用的实例,原因见下面的注解;- MRO 遵循继承图的结构以及子类声明中超类的顺序(先搜索靠前的),使用 C3 算法。
Replacing the old usage of super, calls to the next class in the MRO (method resolution order) can be made without explicitly passing the class object (although doing so will still be supported). Every function will have a cell named class that contains the class object that the function is defined in.
The new syntax
super()is equivalent tosuper(__class__, <firstarg>).While super is not a reserved word, the parser recognizes the use of super in a method definition and only passes in the __class__ cell when this is found. Thus, calling a global alias of super without arguments will not necessarily work.
12.3 多重继承的真实应用
12.4 处理多重继承
- 尽可能区分接口继承与实现继承(代码重用):使用抽象基类显式表示接口,通过混入的方式(混入类不用于实例化,仅供打包方法,且子类不能只继承混入类。常将
...Mixin后缀加入混入类的名称用于区分)重用代码; - 抽象基类可以作为混入,反之不成立;
- 具体类可以没有或最多只有一个具体超类(有时要求一个都没有);
- 抽象基类或混入的组合可以合成聚合类,定义体为空,按固定的顺序继承了各超类;
- 优先使用对象组合,而不是类继承,这样可以提高灵活性,避免强耦合。组合和委托可以代替混入,但不能取代接口继承。
12.5 示例:Django 通用视图中的混入
- 可以尝试使用基于类的视图(使用类来表示视图,后使用类方法
as_view()转换成实例),并根据应用需求扩展它们。这比单块视图函数更合理。