zoukankan      html  css  js  c++  java
  • 流畅的python读书笔记-第十章-继承优缺点

    继承的优缺点

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

    子类化内置类型很麻烦 (如 list 或 dict)) ,别搞这种

    1. 直接子类化内置类型(如 dict、list 或 str)容易出错,因为内置类型的 方法通常会忽略用户覆盖的方法。
    2. 不要子类化内置类型,用户自己定义的类应该继承 collections 模块的类,
    3. 例如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. 把接口继承和实现继承区分开

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

    继承接口,创建子类型,实现“是什么”关系
    继承实现,通过重用避免代码重复

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

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

    现代的 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 模块的类

  • 相关阅读:
    NOIP2011 D1T1 铺地毯
    NOIP2013 D1T3 货车运输 倍增LCA OR 并查集按秩合并
    POJ 2513 trie树+并查集判断无向图的欧拉路
    599. Minimum Index Sum of Two Lists
    594. Longest Harmonious Subsequence
    575. Distribute Candies
    554. Brick Wall
    535. Encode and Decode TinyURL(rand and srand)
    525. Contiguous Array
    500. Keyboard Row
  • 原文地址:https://www.cnblogs.com/twodog/p/12136768.html
Copyright © 2011-2022 走看看