目录
一、继承特性
1.继承的概念
(1)什么是继承
(2)继承中的子类和父类的概念
2.继承的作用
(1)练习1
(2)练习2
3.查看继承的父类
(1)概念
(2)练习
4.方法的复写
(1)概念
(2)练习
5.super()
(1)概念
(2)练习
6.__init__()方法
7.派生属性
8.私有属性私有方法在继承中的表现
9.抽象类
(1)定义抽象类
(2)定义抽象方法
二、多继承
1.语法
2.多继承注意事项
3.继承原理(钻石继承)
4.多继承中super本质
三、多态
继承就是让类和类之间产生父子关系,子类可以拥有父类的静态属性和方法。
[继承就是可以获取另外一个类中的静态属性和普通方法。(并非所有成员)]
注意:python中的继承分为:单继承和多继承。
在python中,新建的类可以继承一个或多个父类
父类:用于被继承的类,称之为父类,也叫做基类,或者超类。
子类:继承其他类的类,称之为子类,也叫做派生类。
继承最大的作用就是提高代码的重用率
创建Dog类和Cat类,分别设置name,gender,age属性和定义eat()、sleep()方法。
并且Dog类在定义一个bite()方法,Cat类定义climb_tree()方法。
class Dog(object):
def __init__(self,name,age,gender):
self.name=name
self.age=age
self.gender=gender
def eat(self):
print('吃。。。')
def sleep(self):
print('睡。。。')
def bite(self):
print('汪汪汪~~~')
def __str__(self):
msg='名字:{},年龄:{},性别:{}'.format(self.name,self.age,self.gender)
return msg
class Cat(object):
def __init__(self,name,age,gender):
self.name=name
self.age=age
self.gender=gender
def eat(self):
print('吃。。。')
def sleep(self):
print('睡。。。')
def climb_tree(self):
print('朕在爬树,请勿打扰。。。')
def __str__(self):
msg='名字:{},年龄:{},性别:{}'.format(self.name,self.age,self.gender)
return msg
dog=Dog('豆豆',6,'公')
print(dog)
dog.eat()
dog.sleep()
dog.bite()
cat=Cat('妙妙',5,'母')
print(cat)
cat.eat()
cat.sleep()
cat.climb_tree()
结果为:
注意:我们发现Cat了和Dog类中有大量的重复代码。
使用继承方式,实现Dog类和Cat类重复代码的重用。
结果为:
格式:类名.__bases__
注意:
(1).python3中如果一个类没有继承任何类,默认继承object类。我们管这种类叫做新式类。
(2).object类,是python中的祖宗,所有的类都是从object类中继承下来的。
查看Cat类和Animal类继承的父类。
查看Cat类和Animal类继承的父类
子类中定义了和父类中相同的方法,我们叫做方法的复写(派生方法)。
实例对象调用此方法的时候就会调用自己类中的方法了。
应用场景:父类的功能满足不了子类,如人类都会吃,但是如学生会去食堂吃,老板会去酒店吃,
这样人类的eat方法就无法满足学生类和老板类,需要复写。
定义一个Person类创建eat()方法,定义一个Student的类,创建eat()方法,
规定学生去食堂吃饭,定义一个Boss类创建eat()方法,规定Boss去酒店吃。
结果为:
子类和父类有相同的方法,如果子类想调用父类相同的的方法。可以使用super()方法。
在python3中,子类执行父类的方法也可以直接用super方法 --->super()
super默认省略了两个参数 第一个参数是类名,第二个参数是self。
两个参数可以省略不传递例如 super(Student,self)
super()还可以从类的外部使用需要传递类名(本类的名称)和对象名
例如 super(Student,student)
格式:
super().方法名称()
父类类名.方法名称(self)
super(本类类名,对象名)
子类调用自己的方法的时候同时调用父类的方法
结果为:
(1)子类继承父类,如果子类不复写父类的__init__()方法,创建子类对象的时候会自动调用父类__init__()方法。
结果为:
(2)子类继承父类,如果子类复习了父类的__init__()方法, 创建的子类对象的时候不会再调用父类的__init__() 方法。
结果为:
(3)注意:
父类的初始化方法有参数,子类初始化无参数,子类再调用父类的参数的时候就会报错。
python要求复写父类的__init__()方法时,需要调用父类的__init__()
class Person(object):
def __init__(self,name):
print('person....')
self.name=name
class Student(Person):
def __init__(self,name):
super().__init__(name)
print('student......')
student=Student('zs')
属性的覆盖(派生属性):子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了(属性的覆盖)。
结果如下:
父类中的私有方法和私有属性都是不能被子类继承下来的,一般父类的属性和方法不会私有
测试父类中的私有属性和私有方法是否能被继承下来
起初,我们定义了Person类实现了eat()、drink()方法,每种人都会吃喝但是吃喝的地点不同,如果实现了方法体就浪费了。
因此我们可以只定义eat()方法,不实现方法体,这种形式我们可以将方法定义为抽象方法,具有抽象方法的类就叫做抽象类。
抽象类是一个特殊的类,只能被继承,不能实例化,抽象类中可以有抽象方法和普通方法。
1、从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,
该类不能被实例化,只能被继承,且子类必须实现抽象方法
2、抽象类中可以定义普通的方法。
3、抽象类也是定义规范。
4、使用抽象类,子类一般都是单继承。
定义抽象类需要导入 abc模块。
from abc import ABCMeta, abstractmethod
抽象方法:只定义方法,不具体实现方法体。
在定义抽象方法时需要在前面加入:@abstractmethod
抽象方法不包含任何可实现的代码,因此其函数体通常使用pass。
注意:子类继承了抽象类父类,子类必须实现父类的抽象方法。
结果为:
一个子类可以继承多个父类,就是多继承,并且拥有所有父类的属性和方法。
例如 孩子会继承自己的父亲和母亲的特征。
class 子类名(父类名1,父类名2…) : pass
如果子类和父类有相同的方法,就会调用子类中的方法。
如果不同的父类中存在着相同的方法名称,子类对象调用的时候会调用哪个父类中的方法呢?
Python会根据 MRO(method resolution order) 方法解析顺序列表进行查找。
提示:开发时,需要避免这种容易产生混淆的情况!--如果父类之间存在同名的属性和方法,应尽量避免使用多继承。
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
注意:D类有两个选择,默认选择B类执行。
在Python2.3之前,MRO是基于深度优先算法的,自2.3开始使用C3算法广度优先,定义类时需要继承object,这样的类称为新式类,否则为旧式类。只有在python3中才有 __mro__ 和 mor() 方法。
不是直接查找父类,而是根据调用节点的广度优先顺序执行的。
练习1:
创建A、B、C、D类,D类继承B,C类,B类继承A类,C类继承A类。
在每个方法中都调用super().func()方法,查看执行顺序。
class A(object):
def test(self):
print('A test...')
class B(A):
def test(self):
super().test()
print('B test...')
class C(A):
def test(self):
super().test()
print('C test...')
class D(B,C):
def test(self):
super().test()
print('D test...')
d=D()
d.test()
print(D.__mro__)
多态就是不同子类对象调用父类的方法产生不同的结果。
声明形参的时候是父类对象,实际运行的时候是子类对象。
练习2:
使用不同的支付工具给商店支付钱
import abc
class Pay(metaclass=abc.ABCMeta):
@abc.abstractmethod
def pay(self,money):pass
class ZfbPay(Pay):
def pay(self,money):
print('支付宝付款{}元'.format(money))
class WxPay(Pay):
def pay(self,money):
print('微信付款{}元'.format(money))
class Person(object):
def consumption(self,pay,money):
pay.pay(money)
per=Person()
zfb=ZfbPay()
wx=WxPay()
per.consumption(zfb,100)
per.consumption(wx,200)