上篇文章分析了类的组合,那么什么时候用继承,什么时候用组合?
用组合:当类之间有显著不同,并且较小的类是较大的类所需要的组件时
用继承:当类之间有很多相同的功能时,可提取这些相同的功能组成基类来继承
子类继承了父类的所有属性,但子类中可以定义与父类中同名的属性,调用时优先使用子类自己定义的属性而不是继承来
的属性,不会对父类的属性有影响。
class Dad:
money= 10
def __init__(self,name):
print('I am father')
self.name= name
def hit_son(self):
print('%s 正在打儿子' %self.name)
class Son(Dad): #Son继承了Dad
pass
print(Dad.__dict__)
print(Son.__dict__) #子类的属性字典里没有父类的属性却可以调用到
print(Son.money) #因为Son继承了Dad所以就有了Dad的那笔钱(数据属性)
#print(Son.hit_son()) #也继承了函数属性,但不能直接用
#实例化之后就可以用了
v = Son('白胡子')
v.hit_son()
print(v.__dict__) #同样,实例中也没有父类的属性字典,但是可以调用
类的继承分为两种。一种叫派生,一种叫接口继承
派生: 子类在继承了之后又定义了新的属性和动作(最好不与父类重名),但派生并不好用,会使代码的耦合性变大,独立性降低,不利于执行
接口继承: 父类中规定好了子类需要实现的方法,但不在父类里面实现,子类继承后才实现
#定义一个基类(用于接口继承的父类)
class All_file:
def read(self):
pass
def write(self):
pass
#继承了基类后,在子类实现read和write的具体方法
#而父类只定义了一个接口,没有实现具体的方法
class Disk(All_file):
def read(self):
print("disk read")
def write(self):
print('disk write')
class Cdrom(All_file):
def read(self):
print("cdrom read")
def write(self):
print('cdrom write')
#不定义write方法
class Mem(All_file):
def read(self):
print('mem read')
v1 = Mem()
v1.read()
v1.write() #但却可以调用write方法,此时调用的就是父类的方法,没有内容
用接口模块abc来实现
import abc #导入abc模块
#定义一个基类(用于接口继承的父类)
class All_file(metaclass=abc.ABCMeta): #必要参数
@abc.abstractmethod
def read(self):
pass
@abc.abstractmethod
def write(self):
pass
#不定义write方法就无法实例化,被严格限制了要使用父类中定义的方法
class Mem(All_file):
def read(self):
print('mem read')
'''def write(self):
pass'''
v1 = Mem()
v1.read()
v1.write()
import abc #导入abc模块
#定义一个基类(用于接口继承的父类)
class All_file(metaclass=abc.ABCMeta): #必要参数
@abc.abstractmethod
def read(self):
pass
@abc.abstractmethod
def write(self):
pass
#不定义write方法就无法实例化,被严格限制了要使用父类中定义的方法
class Mem(All_file):
def read(self):
print('mem read')
'''def write(self):
pass'''
v1 = Mem()
v1.read()
v1.write()
规范子类,即严格控制子类需要基类的那些的功能,才去继承基类(提取出来的父类)
(子类优先于父类查找、深度优先、广度优先原则)
class A:
pass
class B(A):
pass
class C(A):
pass
class D(C):
pass
class E(B,D):
pass
v = E() #实例化E类
print(E.__mro__) #查看E类的继承顺序(里面遵循了深度优先和广度优先原则)
class Teacher:
City= '东京'
def __init__(self,name,salary,subject,age):
self.name= name
self.salary= salary
self.subject= subject
self.age= age
def teach_course(self):
print('{}老师正在教{}课'.format(self.name,self.subject))
class Student(Teacher): #继承Teacher类
def __init__(self,name,salary,subject,age,heigh): #继承的方法中又多传入一个参数
#在父类名调用父类的方法
'''Teacher.__init__(self,name,salary,subject,age) #参数格式和父类的init一样
self.heigh= heigh #使新传入的参数也变成self属性'''
#用super()来调用父类的方法
super().__init__(name,salary,subject,age)
#用super调用参数不需要self,而且父类名有变化仍然可以调用到
self.heigh= heigh #使新传入的参数也变成self属性
'''最直接的调用
self.name= name
self.salary= salary
self.subject= subject
self.age= age
self.heigh= heigh #使新传入的参数也变成self属性'''
#定义一个派生的方法
def show_info(self): #打印信息的一个方法
Teacher.teach_course(self) #调用父类中的方法,参数为self(父类中的self)
print('{}的{}学生正在上{}课'.format(self.heigh,self.name,self.subject))
#实例化,继承父类就按照父类的参数去传参数
st1 = Student('Zoro',2000,'英语',28,'2米以下')
st1.show_info()
print(st1.__class__) #查看对象所在的类
多态的概念:
多态的概念指出了对象如何通过它们共同的属性和动作来操作及访问,而不需要考虑生成它们的类;
多态反映的一种执行时的状态,说白了就是多个子类继承一个父类然后去调用父类的同一个方法;
几乎只要是继承,就会用到多态,所以不需要太纠结多态的概念,把继承学好就够了
#下面用一个水、冰、蒸汽三种H20状态来阐述多态
class H2o:
def __init__(self,name,temperature): #两个参数,名字和温度
self.name= name
self.temperature= temperature
def turn_ice(self): #变成冰
if self.temperature < 0: #如果温度小于0变成冰态
print('【%s】的温度太低,所以变成冰态!' %self.name)
elif self.temperature > 0 and self.temperature < 100: #温度大于0小于100是水态
print('【%s】 液化成水态' %self.name)
elif self.temperature > 100: #温度大于100成水蒸气态
print('【%s】温度高于100度,所以变成水蒸汽态'%self.name)
class Water(H2o): #继承H2o的水态
pass
class Ice(H2o): #继承H2o的冰态
pass
class Steam(H2o): #继承H2o的水蒸气态
pass
#实例化,Water、Ice、Steam子类都继承同一个父类,这里将三个子类实例化
w1= Water('水',33) #名字叫水,温度33度
i1= Ice('冰',-25)
s1= Steam('蒸汽',3500)
#冰、水、蒸汽继承同一个父类,并且调用同一个方法;
#在这里,func函数就代表了turn_ice方法,因为该函数的功能就执行turn_ice
def func(obj):
obj.turn_ice()
#调用func函数,传入的是三个实例化对象,然后再func中调用这三个实例化对象的turn_ice方法
func(w1)
func(i1)
func(s1)