有关继承的更多内容可参考: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()
这表明如果将函数放在基类中(比如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()
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()
- 代码讲解:
- 这里比较重要的是 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 函数继续打印出之后的信息。
- 这里比较重要的是 9-11 行,在 Child 中,当调用 son.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方法
- 举例:
(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()
在这段代码中,我没有使用 Parent 这个名字,因为不存在父子 is-a 关系,而是一个 has-a 关系,其中 Child 有一个(has-a) Other 来完成它的工作。 -
可以看到,Child 和 Other 中的大多数代码都是相同的,可以完成相同的事情。唯一的区别是我必须定义一个 Child.implicit 函数来完成这个动作。然后我可以问自己是否需要这个 Other 作为一个类,我是否可以将它放入一个名为 Other.py 的模块中?
三 什么时候使用继承和组合?
-
“继承与组合”的问题可以归结为试图解决可复用代码的问题。
-
继承通过在基类中创建隐含特性的机制来解决这个问题。
-
组合通过提供模块以及调用其他类中的函数来解决这个问题
-
- 怎么区分使用:
- 无论如何都要避免多重继承,因为它太复杂而且不可靠。如果你被它困住了,那么要准备好了解一下类的层次结构,并花时间找出所有内容的来源。
- 使用组合将代码打包到模块中,这些模块可以用于许多不同的、不相关的地方和情境。
- 只有当存在明显相关的可复用代码片段,并且这些代码片段符合单个通用概念,或者由于你使用了某些东西而别无选择时,你才可以使用继承。
- 关于面向对象编程,需要记住的一点是,它完全是程序员为了打包和共享代码而创建的一种社会约定。因为这是一种社会惯例,并且在 Python 中已经形成了这种惯例,你可能会因为与你一起工作的人而被迫绕过这些规则。在这种情况下,弄明白他们是如何使用每一种东西,然后努力适应这种情况。
- 对象不就是类的拷贝吗? 在某些语言中(如 JavaScript),这是对的。这些被称为原型语言,除了用法之外,对象和类之间没有太多区别。然而,在 Python 中,类充当“铸造”(mint)新对象的模板,类似于使用模具(die) 来铸造硬币的概念。