'''
在python中展现面向对象的三大特征:封装、继承、多态。
前面我们讲了封装,下面我们来学习继承和多态。
'''
目录
一 、析构方法
二、继承
1.单继承
2.多继承
多继承案例
多继承同名方法继承顺序 间接继承
3.重写和调用父类方法
三.属性和方法
1.类属性
2.类方法和静态方法
四、多态
多态概念
多态的利用:“鸭子类型”
一 、析构方法
????????当一个对象被删除或者被销毁时,python解释器也会默认调用一个方法,这个方法为__del__()方法,也称为析构方法
# # __del__()
class Animal:
def __init__(self,name):
self.name=name
print('这是__init__构造初始化方法,构造%s 对象'%(self.name))
pass
def __del__(self): #(定义类时可以不用写)
#主要的应用就是用来操作 对象的释放 一旦释放完毕 对象便不能再使用 (定义类时可以不用写)
print('这是__del__析构方法')
print('当某个作用域下面,没有被使用【引用】的情况下 解释器会自动调用此函数 来释放内存空间')
print('%s 对象被彻底清理了 内存开销也释放了'%(self.name))
pass
cat=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('吃')
pass
def drink(self):
'''
喝
:return:
'''
print('喝')
pass
pass
再让子类去继承这个父类:
class Dog(Animal): # 继承Animal 父类 此时Dog就是子类
def wwj(self):
'''
子类独有的实现
:return:
'''
print('汪汪叫')
pass
class Cat(Animal):
def mmj(self):
print('喵喵叫')
pass
d1=Dog()
d1.eat() # 具有了吃的行为即继承了父类Animal的行为
d1.wwj()
运行结果:
Dog子类也拥有了Animal父类的行为。
2.多继承
子类可以继承一个父类,那是否可以继承两个父类或多个呢?
答案是肯定的,这就是python的多继承
多继承 class 类名(父类1,父类2): ?? ?pass
多继承案例
class Shenxian:
def fly(self):
print('会飞')
pass
class Monkey:
def fly(self):
print('猴子想飞')
def chitao(self):
print('猴子喜欢吃桃')
pass
class Sunwukong(Shenxian,Monkey):
pass
swk=Sunwukong()
swk.fly() # 输出 会飞 而不是 '猴子想飞' 继承顺序 先左右 后辈分(广度优先)
swk.chitao()
运行结果:
Sunwukong这个子类继承了Shenxian,Monkey两个父类
swk.fly()? Shenxian,Monkey两个父类都有fly方法? 输出的是会飞 而不是 '猴子想飞'?
这里涉及到类继承顺序的问题? 【先左右 后辈分(广度优先)】
多继承同名方法继承顺序 间接继承
# 多继承同名方法继承顺序间接继承
class D():
def eat(self):
print('D.eat')
pass
class C(D):
def eat(self): #重写父类方法 [子类方法(再子类中)覆盖了父类同名方法]
print('C.eat')
pass
class B(D):
pass
class A(B,C): # 找B没有 就找C 而不是向上找D
pass
a=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('汪汪叫...')
pass
pass
class AnimAl:
def __init__(self,name,color,ctm):
self.name=name
self.color=color
self.ctm=ctm
class 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=90
self.weight=30
pass
def __str__(self): # 输出对象时按照这个方法打印 没有这个方法不能直接输出
return '{}的颜色会{} 它的身高{}cm 体重是{}kg {}'.format(self.name,self.color,self.height,self.weight,self.ctm)
def bark(self):
super(Kejiquan, self).bark() #调用父类方法
print('叫得很大声...') #属于重写父类方法
pass
kj=Kejiquan('柯基','红色','100') #实例化默认调用父类__init__
kj.bark()
print(kj) # 柯基的颜色会红色 它的身高90cm 体重是90
?运行结果:
?可以看到这里是调用子类的bark方法
三.属性和方法
1.类属性
类属性 就是类对象所拥有的属性
class Student:
pro='上学' #属于类属性 Student类对象所拥有
def __init__(self,age):
self.age=age #实例属性
pass
pass
lm=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 来进行修饰
@classmethod
def get_country(cls):
return cls.country #访问类属性
@classmethod
def change_country(cls,data):
cls.country=data #修改类属性的值 在类方法中
# 静态方法用 @staticmethod 来进行修饰
@staticmethod
def getData(): #静态方法
return People.country
pass
print(People.get_country()) # China 通过类对象去引用
P=People()
print('实例对象访问类方法%s'%P.get_country()) # 实例对象访问类方法China
People.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('我是一个动物...')
pass
pass
class Duck(Animal):
'''
鸭子类 子类【派生类】
'''
def sayWho(self):
print('我是一只鸭子')
pass
class Dog(Animal):
'''
小狗类 子类【派生类】
'''
def sayWho(self):
print('我是一只小狗')
pass
class Cat(Animal):
'''
小猫类 子类【派生类】
'''
def sayWho(self):
print('我是一只小猫,喵喵喵')
pass
duck1=Duck()
duck1.sayWho() #我是一只鸭子
dog1=Dog()
dog1.sayWho() #我是一只小狗 多态
多态的利用:“鸭子类型”
紧接上面的代码:
class People: #未继承Animal
def sayWho(self):
print('我是一个人')
pass
def commonInvoke(obj):
'''
统一调用的方法
:param obj: 对象的实例
:return:
'''
obj.sayWho()
listObj=[Duck(),Dog(),Cat(),People()] # 多态利用 优势
for item in listObj:
'''
循环去调用函数
'''
commonInvoke(item) #'python鸭子类型'(duck typing) 鸭子类型中不关注对象本身,只关注它如何的使用 在其他语言不成立
pass
运行结果:
在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。 “鸭子测试”可以这样表述: “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。” 在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。
|