第十章 单继承、多继承和魔术方法

# 第十章 单继承、多继承和魔术方法
  说起生活中继承的概念,大家可能会说继承财产、继承家业和继承房子等继承别人的事件。那么,在Python编程世界里,类的继承又可以如何理解呢?
  
  所谓类的继承,就是在编写类时,并非要从空白开始。如果你要编写的类是另一个现成类的特殊版本,可使用继承。一个类继承另一个类,它将自动获得另一个类的所有属性和方法。原来的类称为父类,而新类称为子类。从父类继承,可以直接拥有父类的属性和方法,这样可以减少代码,多复用,子类还可以定义自己的属性和方法。
## 一、类的单继承
单继承:一个类只能继承一个父类的方式。
 
### 1、子类的方法__init__()
  创建子类的实例时,Python首先需要完成的任务就是给父类的所有属性赋值。为此,子类的方法__init__需要父类施以缓手。
  
  例如,下面来模拟一个父类为父亲子类为儿子的生活案例。儿子能继承学习父亲的习性,因此我们可以在前面创建的Father类的基础上创建新类Son,这样我们就只需要在继承父亲类后为特殊的属性和行为编写代码。
  
  创建一个简单的Father类。它具备Son类所有功能:
 
执行该代码,结果为:
 
  首先是Father类的代码。创建子类时,父类必须包含在当前文件(Family.py)中,且位于子类前面.定义子类(Son)时,必须在括号内指定父类的名称。第十四行的方法__init__接受创建Father实例所需的信息。
  
  其中,super()是一个特殊的函数,帮助Python将父类和子类关联起来,这行代码让Python调用Son的父类的方法__init__(),让Son实例包含父类所有属性,父类也称为超类(superclass),名称super因此而得名。
  
  第十七行处,我们创建Son类的一个实例,并将其存储在变量在this_son中。调用了Son类中定义的方法__init__(),然后传入实参'yellow'和'black'。
  
  第十八行处,实例化对象this_son能够调用Father类的Behavior方法,继承Father类的行为。
  
### 2、给子类定义属性和方法
  
  让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法。
  
  下面来添加一个儿子特有的属性(age),以及一个描述该属性的方法。我们将存储这个属性,并编写一个打印这个描述该属性的方法:
 
 
执行该代码,结果为:

  在十六行处,我们添加了新属性age,并设置了初始值16,根据Son类创建的实例都将包含这个属性,但所有Father都不包含它。在十八行处,我们还添加了一个名为Age的方法,打印出年龄的信息。

### 3、重写父类的方法
  对于父类的方法,只要它不符合子类模拟的实物的行为,都可对它进行重写。为此,可在子类中定义一个这样的方法,即它与要重写的父类方法同名。这样,Python将不会考虑这个父类方法,而只关注子类中定义的相应方法。
  
  假设Father类有一个名为语文成绩(Chinese)的方法,它对于父类毫无意义。所以你可能需要重写它。如下例:
  
 
  如果想调用方法Chinese(),Python将忽略Father类中的方法Chinese(),转而运行上述代码。使用继承时,可让子类保留从父类那里继承的精华,并剔除不必要的信息。
### 4、将实例用作属性
  使用代码模拟实物时,你可能会发现自己给类添加的字节越来越多,属性方法清单可能越来越长。在这种情况下,可能就需要将类的一部分作为一个独立的类提取出来。你可以将大型类拆分为协同工作的小类。
  
  例如,不断给Son添加细节时,我们会发现其中包含很多同龄人的爱好兴趣的属性和方法信息。在这种情况下,我们可以将这些属性和方法提取出来,放到另外一个名为Young类中,并将一个Young实例用作Son类的一个属性:
   
 
执行该代码,结果为:
  在第一个红框处,我们定义了一个名为Young()的新类,它没有继承任何类。方法__init__()除了self外,还有另外一个形参age。这个形参是可选的:如果没有给它提供值,则默认被设置为16。子类Son的方法Age也被移到这个类中的yong_message()方法。
    
  在第二个红框处,我们添加了一个名为self.age的属性,这行代码让Python创建了一个新的Young实例,并将该实例存储在属性self.age中。每当方法__init__()被调用时,都将执行该操作,因此现在每个Son实例都包含一个自动创建的Yong实例。
  
  在第三个红框处,我们创建了一个变量this_son,要描述年龄信息时,需要使用Yong类的属性age。并且调用Yong实例的方法yong_message()。
  
  这看似做了很多额外的工作,但现在我们想要详细的信息都可以,并不会导致Son混乱不堪!
  
## 二、类的多继承
  多继承:一个类可以继承两个甚至多个父类的方式。当然一个子类有多个直接父类时,该子类会继承得到所有父类的方法。例如即有类A,类B,类C,C同时继承类A与类B,此时C中可以使用A与B中的属性与方法。那么问题来了,如果A与B中具有相同名字的方法,这个时候python调用的会是哪个方法呢?
  
  Python虽然语法上支持多继承,但是却不推荐使用多继承,而是推荐使用单继承,这样可以保证编程思路更清晰,也可以避免不必要的麻烦。
 
执行该代码,结果为:
如上代码,有多个父类包含同名方法会发生什么?此时排在前面的父类中的方法会“遮蔽”后面父类中的方法。  
 
## 三、类的魔术方法
  在Python中,所有以"__"双下划线包起来的方法,都统称为魔术方法,例如类的初始化方法__init__,销毁方法__del__。Python中所有的魔术方法均在官方文档中有相应描述,现在,我们来汇总下常见的魔术方法。
### 1、运算符方法
- __add__(self,other)  #x+y
- __sub__(self,other)  #x-y
- __mlu__(self,other)  #x*y
- __mod__(self,other)  #x%y
- __radd__(self,other)  #y+x
- __rsub__(self,other)  #y-x
- __iadd__(self,other)  #x+=y  
- __isub__(self,other)  #x-=y
- __imlu__(self,other)  #x*=y
- __imod__(self,other)  #x%=y
  执行该代码,结果为:
  
---
17
 
13
 
---
  在以上代码,第①行创建了一个名为MyClass的类,有三个方法,一个初始化方法__init__,两个加法运算方法。在十五行处,我们创建MyClass类的一个实例,并将其存储在变量在obj中,并传入一个实参5。接下来,当Python执行res=obj+2时,即使用对象进行运算相加的时候能够自动触发类中的魔术方法__add__()。
  
  而__add__()和__radd__()的不同就在于对象在运算符的左侧还是右侧,两者相对的位置不同就能触发两种不同的运算方式。那么,其他运算符魔术方法也可以自己课后练习了解一下。
### 2、类型转换方法
- __bool__(self)      #调用bool(对象),将对象强制转为布尔类型
- __complex__(self)   #调用complex(对象),将对象强制转为复数类型
- __float__(self)       #调用int(对象),将对象强制转为浮点类型
- __int__(self)       #调用int(对象),将对象强制转为整数类型
执行该代码,结果为:
  
---
False
 
---
### 3、容器类型方法
  - __len__(self)      #通过len(对象),执行类中的__len__()方法的len(属性)
  - __iner__(self)      #通过iner(对象),执行类中的__iner__()方法,迭代对象
  - __getitem__(self)  #通过getitem(对象),执行类中的__getitem__()方法,通过索引访问对象
执行该代码,结果为:
  
---
2
 
---
### 4、其他魔术方法
  - __class__() 查看类名
  - __base__() 查看继承的父类
  - __bases__() 查看继承的全部父类
  - __dict__() 查看全部属性,返回属性和属性值键值对形式
  - __doc__() 查看对象文档,即类中的注释
  - __dir__() 查看所有属性和方法
  
  还有很多,可以自查资料,了解学习一下。
    
## 四、课堂练习
#### 1、现有如图所示的两个类,定义一个Child类继承这两个类,并且重写__init__方法,在实例化Child时候,调用ParentA类的__init__方法。
 
 
 
#### 2、
 
## 五、上一节课堂练习答案
#### 1、
 
 
执行该代码,结果为:
 
 
#### 2、定义一个类,实例化的时候打印'正在实例化',最后结束的时候,输出'正在销毁'。
 
执行该代码,结果为:
 
---
正在实例化
 
正在销毁
 
---
 
 
  
 
 
 
 
如有问题请留言,谢谢!
原文地址:https://www.cnblogs.com/yunsi/p/13203144.html