'''
在python中展现面向对象的三大特征:封装、继承、多态。
前面我们讲了封装,下面我们来学习继承和多态。
'''
目录
一 、析构方法
二、继承
1.单继承
2.多继承
多继承案例
多继承同名方法继承顺序 间接继承
3.重写和调用父类方法
三.属性和方法
1.类属性
2.类方法和静态方法
四、多态
多态概念
多态的利用:“鸭子类型”
一 、析构方法
当一个对象被删除或者被销毁时,python解释器也会默认调用一个方法,这个方法为__del__()方法,也称为析构方法
# # __del__()class Animal:def __init__(self,name):self.name=nameprint('这是__init__构造初始化方法,构造%s 对象'%(self.name))passdef __del__(self):#(定义类时可以不用写)#主要的应用就是用来操作 对象的释放 一旦释放完毕 对象便不能再使用 (定义类时可以不用写)print('这是__del__析构方法')print('当某个作用域下面,没有被使用【引用】的情况下 解释器会自动调用此函数 来释放内存空间')print('%s 对象被彻底清理了 内存开销也释放了'%(self.name))passcat=Animal('小花猫')print('----------------')dog=Animal('拉布拉多')del dog #可以用del手动删除对象input('程序等待中...') #制造一个暂停 按下回车继续print('--------------------')
运行结果:
可以看到我们使用 del dog 时 dog 对象被__del__方法析构了
按下回车:
小花猫对象在程序运行结束时自动调用了__del__方法
析构方法总结
1、当整个程序脚本执行完毕后会自动调用__del__方法
2、当对像被手动销毁时也会自动调用 __del__ 方法
3、析构函数一般用于资源回收,利用__del__方法销毁对象回收内存等资源
二、继承
# python3 类默认继承object
继承:和现实生活当中的继承是一样的:也就是 子可以继承父的内容【属性和行为】
(爸爸有的儿子都有,儿子有的爸爸不一样有)父类:基类 子类:派生类
代码中其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必 重新编写共有方法
提高效率 减少代码的重复编写 精简代码的层级结构 便于拓展
1.单继承
单继承
class 类名(父类):
pass
先写一个父类:
# 单继承class Animal:def eat(self):'''吃:return:'''print('吃')passdef drink(self):'''喝:return:'''print('喝')passpass
再让子类去继承这个父类:
class Dog(Animal): # 继承Animal 父类 此时Dog就是子类def wwj(self):'''子类独有的实现:return:'''print('汪汪叫')passclass Cat(Animal):def mmj(self):print('喵喵叫')passd1=Dog()d1.eat()# 具有了吃的行为即继承了父类Animal的行为d1.wwj()
运行结果:
Dog子类也拥有了Animal父类的行为。
2.多继承
子类可以继承一个父类,那是否可以继承两个父类或多个呢?
答案是肯定的,这就是python的多继承
多继承
class 类名(父类1,父类2):
pass
多继承案例
class Shenxian:def fly(self):print('会飞')passclass Monkey:def fly(self):print('猴子想飞')def chitao(self):print('猴子喜欢吃桃')passclass Sunwukong(Shenxian,Monkey):passswk=Sunwukong()swk.fly()# 输出 会飞 而不是 '猴子想飞' 继承顺序 先左右 后辈分(广度优先)swk.chitao()
运行结果:
Sunwukong这个子类继承了Shenxian,Monkey两个父类
swk.fly() Shenxian,Monkey两个父类都有fly方法 输出的是会飞 而不是 '猴子想飞'
这里涉及到类继承顺序的问题 【先左右 后辈分(广度优先)】
多继承同名方法继承顺序 间接继承
# 多继承同名方法继承顺序间接继承class D():def eat(self):print('D.eat')passclass C(D):def eat(self): #重写父类方法 [子类方法(再子类中)覆盖了父类同名方法]print('C.eat')passclass B(D):passclass A(B,C): # 找B没有 就找C 而不是向上找Dpassa=A()a.eat() # C.eat继承顺序 先左右 后辈分 (广度优先) 一辈遍历完了 进入上一辈print(A.__mro__) #(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)
运行结果:
__mro__ 方法解析顺序:
在前面的代码中,我们使用了 __mro__ 方法
功能:查询执行顺序。
在执行eat 的方法时 查找方法的顺序是
首先到A-A中没有-去B-B没有-去C(广度优先)找到 如果没找到再去D然后到object 再没找到就报错
3.重写和调用父类方法
# 子类中,有一个和父类相同的名字的方法,在子类中的方法会覆盖吊父类中的同名方法# 为什么要重写:父类的方法已经不满足子类的需求,那么子类就可以重写父类或者 完善父类方法
class Dog:def bark(self):print('汪汪叫...')passpassclass AnimAl:def __init__(self,name,color,ctm):self.name=nameself.color=colorself.ctm=ctmclass Kejiquan(Dog,AnimAl):def __init__(self,name1,color1,ctm1): #属于重写父类方法# 针对这种诉求 我们就需要去调用父类方法#Dog.__init__(self,name,color) #手动调用父类方法 就可以具备name 和color这个两个实例属性了super(Kejiquan, self).__init__(name1,color1,ctm1)#super 是自动找到父类 进而调用方法 多继承时 会按照顺序逐个去找 再调用# 拓展其他的属性self.height=90self.weight=30passdef __str__(self): # 输出对象时按照这个方法打印 没有这个方法不能直接输出return '{}的颜色会{} 它的身高{}cm 体重是{}kg {}'.format(self.name,self.color,self.height,self.weight,self.ctm)def bark(self):super(Kejiquan, self).bark() #调用父类方法print('叫得很大声...')#属于重写父类方法passkj=Kejiquan('柯基','红色','100') #实例化默认调用父类__init__kj.bark()print(kj)# 柯基的颜色会红色 它的身高90cm 体重是90
运行结果:
可以看到这里是调用子类的bark方法
三.属性和方法
1.类属性
类属性 就是类对象所拥有的属性
class Student:pro='上学' #属于类属性 Student类对象所拥有def __init__(self,age):self.age=age #实例属性passpasslm=Student(18)print(lm.age,lm.pro) # 18 上学 pro 先找自己的实例属性 再找类属性lm.pro='上班' # lm变(成了实例属性了) xh不变print(lm.age,lm.pro) # 18 上学 pro 先找自己的实例属性 再找类属性xh=Student(21)print(xh.pro) #上学print('------------通过类对象Student访问类对象-------------')print(Student.pro)# 类名.类属性 上学Student.pro='业务' #更改类属性print(xh.pro,lm.pro) # 业务 上班 xh变了 lm维持上次变化
运行结果:
类属性是可以被类对象和实例对象共同访问使用。实例属性只能被实例对象访问使用。
但是当实例对象的实例属性与类属性同名时,实例属性优先。
2.类方法和静态方法
# 类方法 # 静态方法class People:country='China'# 类方法用 @classmethod 来进行修饰@classmethoddef get_country(cls):return cls.country #访问类属性@classmethoddef change_country(cls,data):cls.country=data #修改类属性的值 在类方法中# 静态方法用 @staticmethod 来进行修饰@staticmethoddef getData(): #静态方法return People.countrypassprint(People.get_country()) # China 通过类对象去引用P=People()print('实例对象访问类方法%s'%P.get_country()) # 实例对象访问类方法ChinaPeople.change_country('英国')print('----------修改后-----------')print(People.get_country()) # 英国print(P.get_country()) # 英国print(People.getData()) #英国 调用静态方法print(P.getData())# 注意一般情况下 不用实例对象去访问静态方法
运行结果:
类方法可以被类对象和实例对象使用。实例方法只能被实例对象使用。
为什么要使用静态方法呢:
由于静态方法主要来存放逻辑性的代码,本身和类以及实例对象没有交互
也就是说,在静态方法中,不会设计到类中方法和属性的操作
数据资源能够得到有效的充分利用
四、多态
多态概念
所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态。
Pyhon不支持Java和C#这一类语言中多态的写法,但是原生多态,Python崇尚“鸭子类型”,利用python伪代码实现Java和C#的多态
# 多态class Animal:'''父类【基类】'''def sayWho(self):print('我是一个动物...')passpassclass Duck(Animal):'''鸭子类 子类【派生类】'''def sayWho(self):print('我是一只鸭子')passclass Dog(Animal):'''小狗类 子类【派生类】'''def sayWho(self):print('我是一只小狗')passclass Cat(Animal):'''小猫类 子类【派生类】'''def sayWho(self):print('我是一只小猫,喵喵喵')passduck1=Duck()duck1.sayWho() #我是一只鸭子dog1=Dog()dog1.sayWho() #我是一只小狗 多态
多态的利用:“鸭子类型”
紧接上面的代码:
class People: #未继承Animaldef sayWho(self):print('我是一个人')passdef commonInvoke(obj):'''统一调用的方法:param obj: 对象的实例:return:'''obj.sayWho()listObj=[Duck(),Dog(),Cat(),People()] # 多态利用 优势for item in listObj:'''循环去调用函数'''commonInvoke(item)#'python鸭子类型'(duck typing) 鸭子类型中不关注对象本身,只关注它如何的使用 在其他语言不成立pass
运行结果:
在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。 “鸭子测试”可以这样表述: “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。” 在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。
小白Python学习记录Day8 面向对象基础(中) (析构方法 单继承 多继承 继承的传递 重写父类方法 调用父类方法 类属性的实例属性 类方法和静态方法 多态 )