一、继承与派生 inheritance 和 derived
1.什么是继承或者派生
继承是从已有的类中派生出新的类,新类具有原类的数据属性和行为,并扩展新的能力
派生就是从一个已有的类衍生出来的新类,在原来的类的基础上添加新的属性或者方法
作用:
用继承派生机制,可以将一些共有功能加在基类上,实现代码复用
在不改变超类的代码的基础上,改变原有的功能
名词:
基类(base class)/超类(super class)/父类(father class):一个概念
派生类(derived class)/子类(child class)
2.单继承:
class 类名(父类名):
语句块
示例:
class Human: #人....
def say(self,words): #说话行为
print("说了:",words)
def walk(self,distance):#走路行为
print("走了:",distance,"公里")
h1 = Human()
h1.say("今天天气真好")
h1.walk(50)
print("------------------------------------------------")
#创建一个学生类,多一个学习的行为
class Student(Human):
def study(self,subject):
print("今天学的科目是",subject)
s1 = Student()
s1.say("今天天气真好") #直接调用父类的方法
s1.walk("100")
s1.study("物理")
执行结果:
说了: 今天天气真好
走了: 50 公里
------------------------------------------------
说了: 今天天气真好
走了: 100 公里
今天学的科目是 物理
#设计一个teacher类,要求包含学习和教学的行为
class teacher(Student): #既继承了human也继承了student
def teach(self,subject):
print("我教书的科目是:",subject)
t = teacher()
t.say("good")
t.walk(199)
t.study("math")
t.teach("python")
执行结果:
说了: good
走了: 199 公里
今天学的科目是 math
我教书的科目是: python
注:
student对象找相应方法从自身开始,自下而上,找到Human类中的同名方法,将student类的对象传给对应的方法,即可。
练习:
我们知道内建模块中的list只有append向末尾添加元素的方法,但没有向列表首部添加的方法,能否设计一个类,实现Insert_head(n),在列表头插入元素
class mylist(list):
def insert_head(self,element):
self.insert(0,element)
l = mylist()
l.append(1)
l.append(2)
l.insert_head(9)
for x in l:
print(x,end = ' ')
执行结果:
9 1 2
继承说明:
任何类都可以直接或者间接的继承自object类
object类是一切类的超类
类内的__base__属性:
此属性来记录此类的基类是谁
见:help(builtins)
示例:
class Human: #这里什么类都没有继承 默认是继承自object类
def say(self,words): #说话行为
print("说了:",words)
def walk(self,distance):#走路行为
print("走了:",distance,"公里")
class Student(Human):
def study(self,subject):
print("今天学的科目是",subject)
class teacher(Student):
def teach(self,subject):
print("我教书的科目是:",subject)
help(Human.__base__)
help(Student.__base__)
help(teacher.__base__)
执行结果:
class Human(builtins.object)
class Student(Human)
.....
二、覆盖(重写):override
什么是覆盖:
覆盖是指在有继承派生关系的类中,子类中实现了与基类同名的方法,在子类对象调用方法的时候,实际调用的是子类中的同名方法,这种现象叫覆盖
(同名就会发生覆盖,其实可以这样理解,同名的方法一般是自己创建的方法是最合适自己的,所以一定优先调用自己创建的方法了,自己有自己的想法 -_-)
class A:
def work(self):
print("A")
class B(A):
def work(self):
print("B")
b = B()
b.work()
#既然我自己定义了,肯定是父类的不太适合我,我肯定优先调用我自己,所以子类就把父类的方法覆盖掉了
问题:当覆盖发生时,如果子类非要调用父类的同名方法怎么办:
有两种方法:
1.“指名道姓”的调用父类的方法
2.super()函数
super:
super(子类type,子类obj):把子类对象的类型当成父类的类型来调用方法
返回绑定超类的实例,要求obj必须是type类型实例
super()返回绑定超类的实例,等同于super(class,实例的第一个参数),且必须在方法内调用
方法1:
class A:
def work(self):
print("A")
class B(A):
def work(self):
print("B")
b = B()
A.work(b) #指名道姓的要调用父类的方法,解释器能明白子类的意思
方法2:原本对象找方法是从自身向上找,super修改为从父类开始查找
class A:
def work(self):
print("A")
class B(A):
def work(self):
print("B")
#super函数来访问被覆盖掉的父类方法,
b = B()
b.work()
#优先从父类的方法开始查找
super(B,b).work()
执行结果:
B
A
示例:演示如何用super函数让子类显示调用父类的种被覆盖的函数
class A:
def work(self):
print("A")
class B(A):
def work(self):
print("B")
def doworks(self):
#self.work() #当然是调用B的work,能执行到这里说明self是B类型
super(B,self).work()#调用父类的work方法
super().work() #调用父类的方法
b = B()
b.work()
b.doworks()
执行结果:
B
A
A
说明:
当子类种实现了__init__方法后,父类的__init__方法将被覆盖,即不再会主动的调用父类的__init__方法,会引起父类的属性得不到初始化,此时我们要显式的调用父类的初始化函数。
示例:
class Human:
def __init__(self,n,a):
self.name = n
self.age = a
print("Human种的init函数被调用")
def show_info(self):
print("姓名:",self.name)
print("年龄:",self.age)
class Student(Human):
def __init__(self,n,a,s = 0):
super().__init__(n,a) #显式调用父类的初始化方法
#super(Human,self)._init_(n,a) #也可以这样显式的调用
self.score = s
print("Student中的init函数被调用")
def show_info(self):
super().show_info() #调用父类中的show_info
print("成绩为:",self.score)
#上述子类种init和show_info函数把父类的所有同名函数都给覆盖了
#导致父类的初始化函数不能被执行,
#也就是说子类对象中的父类部分没有初始化,所以我们需要在子类的初始化中显式的调用父类的初始化函数
s1 = Student("安琪拉",500,99)
执行结果:
Human种的init函数被调用
Student中的init函数被调用