第十一章 面向对象的进阶
面向对象的进阶
1.继承介绍
1.1.什么是继承
继承一种新建类的方式,新建的类称之为子类/派生类,被继承的类称之为父类\基类\超类
python中继承的特点:
子类可以遗传/重用父类的属性
1.2.为何要用继承
减少类与类之间代码冗余
1.3.如何用继承
语法
class 父类:
pass
class 子类(父类):
pass
实例
class Parent1:
xxx = 333
def run(self):
print('我是父类的方法')
class Sub1(Parent1):
# xxx = 222
# def run(self):
# print('我是子类的方法')
pass
obj = Sub1()
# obj.xxx=111
# 对象 << 类 << 父类
print(obj.xxx)
obj.run()
2.利用继承来解决类和类之间的代码冗余问题
我们先思考一个问题:请看图
1.对象和类的关系
奥巴马和梅西都有人的特征,麦兜和猪坚强都有猪的特征,史努比和史派克都有狗的特征
所以总结对象的相似之处得到了类
2.类和父类的关系
人,猪和狗都有动物的特征
所以总结类的相似之处得到父类
例子
学生类和老师类都是人,所以他们具有人相同的特征
# @Author : 大海
# @File : 2.利用继承来解决类和类之间的代码冗余问题.py
'''
总结对象的相似之处得到了类
总结类的相似之处得到父类
'''
class People:
school = '图灵学院'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class Student(People):
# def __init__(self,name,age,sex):
# self.name = name
# self.age = age
# self.sex = sex
def play(self):
print('%s play football' % self.name)
class Teacher(People):
# def __init__(self,name,age,sex):
# self.name = name
# self.age = age
# self.sex = sex
def course(self):
print('%s course'%self.name)
# 实例化的时候子类没有__init__方法会调用父类的
stu1 = Student('周阳',30,'male')
print(stu1.__dict__)
tea1 = Teacher('大海',31,'man')
print(tea1.__dict__)
# 但是这里有个问题子类有新的属性需要实例化的时候参数怎么办
3.子类派生的新方法中重用父类功能的方式一
3.1.什么是派生
指的是子类继承父类的属性和方法,并且派生出自己独有的属性与方法。
3.2.为什么要派生
子类可以派生出自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找,
例如每个学生还有分数这一属性,我们就需要在Student类中定义该类自己的
__init__方法去覆盖父类的(因为优先用子类的方法)
3.2.子类派生的新方法中重用父类功能的方式一
指名道姓地引用某一个类中的函数(与继承无关)
# @Author : 大海
# @File : 3.子类派生的新方法中重用父类功能的方式一.py
'''
子类重用父类的功能
在子类派生出的新方法中重用父类功能的方式一:
指名道姓地引用某一个类中的函数
'''
class People:
school = '图灵学院'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class Student():
def __init__(self,name,age,sex,score=0):
# self.name = name
# self.age = age
# self.sex = sex
# 调用的是函数,因而需要传入self参数
People.__init__(self,name,age,sex)
self.score=score
def play(self):
print('%s play football' % self.name)
class Teacher():
def __init__(self,name,age,sex,hobby):
# self.name = name
# self.age = age
# self.sex = sex
People.__init__(self, name, age, sex)
self.hobby = hobby
def course(self):
print('%s course'%self.name)
stu1 = Student('周阳',30,'male',100)
print(stu1.__dict__)
tea1 = Teacher('大海',31,'man','篮球')
print(tea1.__dict__)
3.3.在子类派生的新方法中重用父类功能的方式二
super()专门用来引用父类的属性(与继承有关)
# @Author : 大海
# @File : 6.在子类派生的新方法中重用父类功能的方式二.py
'''
派生实例化除了父类的属性添加,还能有自己独有的属性 ******
在子类派生出的新方法中重用父类功能的方式二:super()必须在类中用
super(自己的类名,自己的对象)
可以省略传值
super()
'''
class People:
school = '图灵学院'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class Student(People):
def __init__(self,name,age,sex,score=0):
# 相当于调用了父类的方法
# 指名道姓People
# People.__init__(self,name,age,sex)
# self.score=score
# 必须要继承 Student父类的
super(Student,self).__init__(name,age,sex)
self.score=score
def play(self):
print('%s play football' % self.name)
class Teacher(People):
def __init__(self,name,age,sex,hobby):
# People.__init__(self, name, age, sex)
# self.hobby = hobby
super().__init__(name, age, sex)
self.hobby = hobby
def course(self):
print('%s course'%self.name)
stu1 = Student('周阳',30,'male',100)
print(stu1.__dict__)
tea1 = Teacher('大海',31,'man','篮球')
print(tea1.__dict__)
4.在单继承背景下的属性查找
4.1.在单继承背景下的属性查找顺序
# @Author : 大海
# @File : 4.在单继承背景下的属性查找.py
# 在单继承背景下属性的查找优先级:对象->对象的类->父类->父类.....->object
class Foo():
xxx = 444
pass
class Bar1(Foo):
xxx = 333
pass
class Bar2(Bar1):
xxx = 222
pass
obj = Bar2()
# obj.xxx=111
print(obj.xxx)
例子
class Foo:
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
# obj.f1()是Bar()的对象
self.f1()
class Bar(Foo):
def f1(self):
print('Bar.f1')
obj=Bar()
obj.f2()
5.在多继承背景下的属性查找(了解)
广度优先查找
例子
# @Author : 大海
# @File : 5.在多继承背景下的属性查找.py
# 在多继承背景下属性的查找优先级: **
# 此时属性的查找优先级是:对象->对象的类->按照从左往右的顺序一个分支一个分支的找下去
# 广度优先查找,从左往右一个分支一个分支的查找,在最后一个分支才去查找顶级类
# 第四层 # 顶级类是在最后一个分之才走的
class G():
x = 'G'
pass
# 第三层
class E(G):
# x = 'E'
pass
class F(G):
# x='F'
pass
# 第二层
class B(E):
# x= "B"
pass
class C(F):
# x='C'
pass
class D(G):
# x='D'
pass
# 第一层
class A(B,C,D):
# x = 'A'
pass
obj = A()
# obj.x = 111
print(obj.x)
# python专门为继承类内置了一个mro的方法,用来查看c3算法的计算结果
print(A.mro())
6.组合
6.1.什么是组合
组合指的是某一个对象拥有一个属性,该属性的值是另外一个类的对象
class Foo:
xxx = 100
class Bar:
yyy = 200
def zzz(self):
print('我是bar方法')
obj = Foo()
obj1 = Bar()
# # obj.attr = '大海'
# # print(obj.attr)
obj.attr = obj1
# # obj.attr 等价于 obj1
print(obj.attr)
print(obj1)
print(obj.attr == obj1)
print(obj1.yyy)
# # obj.attr相当于 Bar的对象 obj1
print(obj.attr.yyy)
obj1.zzz()
obj.attr.zzz()
6.2.为何要用组合
通过为某一个对象添加属性(属性的值是另外一个类的对象)的方式,可以间接地将两个类关联/整合/组合到一起
从而减少类与类之间代码冗余
6.3.为何要用组合
class People:
school = '图灵学院'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class Student(People):
def __init__(self, name, age, sex,score=0):
super().__init__(name, age, sex)
self.score = score
class Teacher(People):
def __init__(self,name,age,sex,course=[]):
super().__init__(name, age, sex)
self.course=course
def tell_info1(self):
# 这个时候是列表里面装了4个课程对象
# print(self.course)
for i in self.course:
# print(i)
# 课程属性
# print('<课程名:%s,价格%s>' % (i.c_name, i.c_price))
# 课程方法
i.tell_info()
class Course:
def __init__(self,c_name,c_price):
self.c_name = c_name
self.c_price = c_price
def tell_info(self):
print('<课程名:%s,价格%s>'%(self.c_name,self.c_price))
python = Course('python开发',10000)
math = Course('高等数学',3000)
English = Course('英语',4000)
music = Course('音乐',5000)
drawing = Course('绘画',6000)
dahai = Teacher('大海',24,'man')
# print(dahai)
# 把music课程对象添加到老师对象dahai的属性里面
# dahai.course = music
# print(dahai.course)
# print(music)
# dahai.course.tell_info()
# dahai.course = math
# dahai.course.tell_info()
# 如果我想把一堆的对象放到一个对象属性里面,我们怎么做?
# 思考一下? 放到一个容器里面 列表
# print(dahai.course)
# dahai.course = music
# print(dahai.course)
dahai.course.append(python)
dahai.course.append(math)
dahai.course.extend([English,music])
# 这个时候是列表里面装了4个课程对象
print(dahai.course)
print('===================')
dahai.tell_info1()
7.组合和继承的区别
'''
组合和继承的区别,继承是把全部的属性和方法让子类可以调用,
而组合只是部分的属性和方法把2个类关联到一起,有些类的属性
不能全部继承,这就用到了组合,组合的是对象,而继承的是类,
继承是在类定义产生的,它是类之间的关联关系,
而组合是类定义后产生的关系,因为它是对象的关联关系
'''
8.多态
8.1.什么是多态
多态指的是同一种/类事物的不同形态,比如动物有多种形态:猫,狗,猪。
8.2.为何要用多态
多态性:在多态的背景下,可以在不用考虑对象具体类型的前提下而直接使用对象
多态性的精髓:统一
8.3.如何用多态
1.定义一个多态性的方法
class Animal:
# 动物都会叫
def speak(self):
print('我是动物我会说话')
class People(Animal):
def speak(self):
print('say hello')
class Dog(Animal):
def speak(self):
print('汪汪汪')
class Pig(Animal):
def speak(self):
print('哼哼哼')
# 三个对象都是动物
obj1=People()
obj2=Dog()
obj3=Pig()
2.多态性的精髓:统一
多态性的好处在于增强了程序的灵活性和可扩展性,比如通过继承Animal类创建了一个新的类型,实例化得到的对象obj,可以使用相同的方式使用object.speak()
# 多态性的精髓:统一
# 学车,学的是小汽车标准,会了所有的小汽车都会开
obj1.speak()
obj2.speak()
obj3.speak()
# 内置方法多态方法
# len()
# 计算多个值的长度
3.python中一切皆对象,数据类型本身就是对象,本身就支持多态性
print(len([1, 2, 3, 4]))
print(len('1234'))
print(len((1, 2, 3, 4)))
# 计算多个值的长度,如果按照以下这样就缺乏了多态性,灵活性就弱
# size()
# zhangdu()
# kuangdu()
9.常用的魔法方法
9.1.__ str __
在对象被打印时自动触发,可以用来定义对象被打印时的输出信息
class People:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return '<name:%s age%s>'%(self.name,self.age)
def run(self):
return self.name
obj = People('大海',18)
print(obj)
print(obj.run())
9.2.__ del __
在对象被删除时先自动触发该方法,可以用来回收对象以外其他相关资源,比如系统资源
# # __del__
# import time
class Foo:
def __init__(self,x,filepath,encoding='utf-8'):
self.x=x
self.f=open(filepath,'rt',encoding=encoding)
def __del__(self):
print('__del__正在运行')
self.f.close()
obj = Foo(1,'a.txt')
# # print(obj.f.read())
# # obj.f.close()
# print('不是对象生成运行__del__,对象正在使用的时候不会运行')
# time.sleep(3)
# 对象使用不会运行__del__
# print(obj.f.read())
9.3.__ call __
在对象被调用时会自动触发该方法
# __call__: 在对象被调用时会自动触发该方法
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
def __call__(self, *args, **kwargs):
print(self,args,kwargs)
def run(self):
print('run')
obj1 = Foo(1,2)
obj1.run()
# 没写__call__不可以调用
# obj1()
# 写了__call__可以调用
# obj1()
# obj1(1,2,x=3,y=4)
# # 省略了
# obj1.__call__(1,2,x=3,y=4)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了