练习44-继承和组合

有关继承的更多内容可参考:https://www.cnblogs.com/luoxun/p/13468850.html

一  继承

(1)什么是继承?

  • 功能:继承用来表明一个类将从其父类那里获得大多数或所有特性。
  • 交互方式:
    • 对子类的行为意味着对父类的行为。
    • 子类上的操作会覆盖父类上的操作。
    • 子类上的操作会更改父类上的操作。
  • 隐式继承(Implicit Inheritance):
    • 举例:
       1 class Parent(object):
       2     
       3     def implicit(self):
       4         print("PARENT implicit()")
       5 
       6 class Child(Parent):
       7     pass
       8 
       9 dad = Parent()
      10 son = Child()
      11 
      12 dad.implicit()
      13 son.implicit()
      ex44a.py
      这表明如果将函数放在基类中(比如Parent),然后所有子类(比如 Child)会自动获得这些特性。
    • 隐式调用函数的问题在于,有时你希望子类的行为有所不同。
  • 显示继承(Override Explicitly):
    • 举例:
       1 class Parent(object):
       2     
       3     def override(self):
       4         print("PARENT override()")
       5 
       6 class Child(Parent):
       7     
       8     def override(self):
       9         print("CHILD override()")
      10 
      11 dad = Parent()
      12 son = Child()
      13 
      14 dad.override()
      15 son.override()
      ex44b.py
      Child 通过定义它自己的版本来重写了那个函数。
  • 修改前后(使用super调用父类的方法)
    • 举例:
       1 class Parent(object):
       2     
       3     def altered(self):
       4         print("PARENT altered()")
       5 
       6 class Child(Parent):
       7     
       8     def altered(self):
       9         print("CHILD BEFORE PARENT altered()")
      10         super(Child,self).altered()
      11         print("CHILD AFTER PARENT altered()")
      12 
      13 dad = Parent()
      14 son = Child()
      15 
      16 dad.altered()
      17 son.altered()
      ex44c.py
    • 代码讲解:
      • 这里比较重要的是 9-11 行,在 Child 中,当调用 son.altered() 时,我其实做了以下事情:
        • 1. 因为在 Child.altered 版本运行时,我就重写了 Parent.altered 。第 9 行就按照你的预期执行了。
        • 2. 在这个例子中,我想做一个之前和之后的对比,所以在第 9 行之后,我想使用super 来获得 Parent.altered 版本。
        • 3. 在第10行,我调用 super(Child,self).altered() ,它意识到需要继承,并会为你获取 Parent 类。你应该能够把这个理解为“使用参数 Child 和 self 来调用 super,然后在它返回的任何地方调用 altered 函数”。
        • 4. 此时,Parent.altered 版本的函数运行,并打印出 Parent 的信息。
        • 5. 最后,它从 Parent.altered 返回。Child.altered 函数继续打印出之后的信息。
  • 三者结合
    • 举例:
       1 class Parent(object):                  # 父类
       2     
       3     def implicit(self):                # 用来表示隐式继承的方法,在子类中没有被重写
       4         print("PARENT implicit()")
       5     
       6     def override(self):                # 用来表示显示继承的方法,在子类中被重写,但是没有被通过super方法调用
       7         print("PARENT override()")
       8     
       9     def altered(self):                 # 用来表示显示继承的方法,在子类中不仅被重写还被super方法调用了
      10         print("PARENT altered()")
      11 
      12 class Child(Parent):                   # Child是子类,继承了Parent类,即Parent类中所有的方法Child类都可以直接调用
      13     
      14     def override(self):                # 重写父类中的override方法
      15         print("CHILD override()")
      16     
      17     def altered(self):                 # 重写父类中的altered方法
      18         print("CHILD BEFORE PARENT altered()")
      19         super(Child,self).altered()    # 使用super方法调用了父类的altered方法
      20         print("CHILD AFTER PARENT altered()")
      21 
      22 dad = Parent()                 # dad是父类Parent的对象
      23 son = Child()                  # son是子类Child的对象
      24 
      25 dad.implicit()                 # 通过父类类对象dad掉用父类中的implicit方法
      26 son.implicit()                 # 通过子类类对象son调用父类中的implicit方法
      27 
      28 dad.override()                 # 通过父类类对象dad掉用父类中的override方法
      29 son.override()                 # 通过子类类对象son调用子类中的override方法,该方法父类中也存在,但子类中对它进行了重写;此时子类的类对象son已经无法直接调用父类的override方法了
      30 
      31 dad.altered()                  # 通过父类类对象dad掉用父类中的altered方法
      32 son.altered()                  # 通过子类类对象son调用子类中的altered方法,子类对父类的altered方法进行了重写,并在重写过程中用super方法调用了父类的方法,同时子类的类对象son无法直接调用父类的altered方法
      ex44d.py

(2)super方法

  • 多重继承:指你定义了一个继承自一个或多个类的类
  • 语法:
    1 class SuperFun (Child, BadStuff):
    2     pass
  • 问题:在这种情况下,每当你对任何 SuperFun 的实例执行隐式操作时,Python 都必须在 Child 类和BadStuff 类的层级结构中查找可能的函数,不过它需要以一致的顺序来执行这项操作。
  • 解决办法:Python 使用了“方法解析顺序”(method resolution order,MRO)和一种被称为 C3 的算法;但它非常复杂,Python不能让你自己来处理 MRO。
  • 最终结果:提供了 super() 函数,它可以在你需要更改类似动作的地方为你解决这个问题;使用 super() ,你不用担心是否正确,Python 会为你找到正确的函数。
  • 常用用法:——用 __init__ 来使用 super()
    • 用途:super() 最常用的用法其实是在基类中使用 __init__ 函数。这通常是你在一个子类中唯一需要做一些操作,然后在父类中完成初始化的地方。
    • 举例:
      1 class Child (Parent):
      2 
      3     def __init__(self, stuff):
      4         self.stuff = stuff
      5         super(Child, self).__init__( ) 

二 组合

  • 组合:继承很有用,但是还有一种能实现相同效果的方法,就是使用其他类和模块,而不是依赖于隐式继承。
  • 举例:
     1 class Other(object):
     2     
     3     def implicit(self):
     4         print("OTHER implicit()")
     5     
     6     def override(self):
     7         print("OTHER override()")
     8     
     9     def altered(self):
    10         print("OTHER altered()")
    11 
    12 class Child(object):
    13     
    14     def __init__(self):
    15         self.other = Other()
    16     
    17     def implicit(self):
    18         self.other.implicit()
    19     
    20     def override(self):
    21         print("CHILD override()")
    22     
    23     def altered(self):
    24         print("CHILD BEFORE PARENT altered()")
    25         self.other.altered()
    26         print("CHILD AFTER PARENT altered()")
    27 
    28 son = Child()
    29 
    30 son.implicit()
    31 son.override()
    32 son.altered()
    ex44e.py
    在这段代码中,我没有使用 Parent 这个名字,因为不存在父子 is-a 关系,而是一个 has-a 关系,其中 Child 有一个(has-a) Other 来完成它的工作。
  • 可以看到,Child 和 Other 中的大多数代码都是相同的,可以完成相同的事情。唯一的区别是我必须定义一个 Child.implicit 函数来完成这个动作。然后我可以问自己是否需要这个 Other 作为一个类,我是否可以将它放入一个名为 Other.py 的模块中?

三 什么时候使用继承和组合?

  • “继承与组合”的问题可以归结为试图解决可复用代码的问题。
    • 继承通过在基类中创建隐含特性的机制来解决这个问题。
    • 组合通过提供模块以及调用其他类中的函数来解决这个问题
  • 怎么区分使用:
    • 无论如何都要避免多重继承,因为它太复杂而且不可靠。如果你被它困住了,那么要准备好了解一下类的层次结构,并花时间找出所有内容的来源。
    • 使用组合将代码打包到模块中,这些模块可以用于许多不同的、不相关的地方和情境。
    • 只有当存在明显相关的可复用代码片段,并且这些代码片段符合单个通用概念,或者由于你使用了某些东西而别无选择时,你才可以使用继承。
  • 关于面向对象编程,需要记住的一点是,它完全是程序员为了打包和共享代码而创建的一种社会约定。因为这是一种社会惯例,并且在 Python 中已经形成了这种惯例,你可能会因为与你一起工作的人而被迫绕过这些规则。在这种情况下,弄明白他们是如何使用每一种东西,然后努力适应这种情况。
  • 对象不就是类的拷贝吗? 在某些语言中(如 JavaScript),这是对的。这些被称为原型语言,除了用法之外,对象和类之间没有太多区别。然而,在 Python 中,类充当“铸造”(mint)新对象的模板,类似于使用模具(die) 来铸造硬币的概念。
posted @ 2020-08-11 17:01  洛兰123  阅读(160)  评论(0编辑  收藏  举报