【Python进阶】9.面向对象高级
1 定义类的几种语法
方式1:类名
在上一章节的学习过程中,我们都使用了这种定义类的语法:
class 类名:
代码
...
例如,使用该方式来定义一个老师类。
# 1.class 类名:
# pass
class Teacher:
pass
方式2:类名()
在编写类时,也可以写成如下格式(旧式类):
class 类名():
代码
...
例如,使用该方式来定义一个老师类。
# # 2.class 类名():
# # pass
class Teacher():
pass
方式3:类名(object)
此外,还有一种更为常见的定义类的语法(新式类),如下:
class 类名(object):
代码
...
例如,使用该方式来定义一个老师类。
# 3.class 类名(object):
# pass
class Teacher(object):
pass
推荐使用第三种类的定义方式
2 python中的继承
2.1 什么是继承
生活中的继承: 一般指的是子女继承父辈的财产。
面向对象中的继承: 指的是多个类之间的所属关系,即子类默认继承父类的属性和方法。
作用: 提高代码的复用率, 减少重复代码的书写。
在Python中,继承形式如下:
class 父类名(object):
...(省略)
class 子类名(父类名): # 继承语法
...(省略)
例如,Father类有一个默认性别为男,且爱好散步行走,那么,Son类也想要拥有这些属性和行为,该怎么做呢?
"""
继承介绍:
概述:
现实中的继承指的是: 子承父业, 编程中的继承指的是: 子类从父类继承过来 属性 和 行为.
格式:
class 子类名(父类名):
pass
例如:
class A(B):
pass
叫法:
类A: 子类, 派生类, 扩展类.
类B: 父类, 基类, 超类.
好处:
提高代码的复用性.
细节:
所有的类都直接或者间接继承自 object类, 它是所有类的父类, 基类.
"""
# 案例: 定义父类Father类, 属性: 性别=男, 行为: 散步. 定义子类Son, 继承父类, 创建对象, 并调用.
# 1. 定义Father类, 充当父类.
class Father(object):
# 1.1 定义属性.
def __init__(self):
self.gender = '男'
# 1.2 定义行为
def walk(self):
print('饭后走一走, 活到99!')
# 2. 定义子类Son类, 充当子类.
class Son(Father): # 继承关系 Son -> Father -> object
pass
# 在main方法中进行测试.
if __name__ == '__main__':
# 3. 创建子类对象.
s = Son()
# 4. 尝试调用 从父类继承过来 的内容(属性 和 行为)
print(s.gender)
s.walk()
继承的相关概念: 继承:一个类从另一个已有的类获得其成员的相关特性,就叫作继承! 派生:从一个已有的类产生一个新的类,称为派生! 很显然,继承和派生其实就是从不同的方向来描述的相同的概念而已,本质上是一样的! 父类:也叫作基类,就是指已有被继承的类! 子类:也叫作派生类或扩展类
2.2 单继承
单继承就是一个子类继承一个父类。
语法:
class 类名(父类名):
代码
...
故事1:
一个摊煎饼的老师傅[master],在煎饼果子界摸爬滚打多年,研发了一套精湛的摊煎饼技术, 师父要把这套技术传授给他的唯一的最得意的徒弟[apprentice]。[初始化、无参]
# 单继承指的是: 类只能继承自另外的1个类, 从中继承过来 属性 和 行为.
# 故事1: 一个摊煎饼的老师傅, 在煎饼果子界摸爬滚打多年, 研发了一套精湛的摊煎饼的奇数. 他(老师傅)要传授这个套技术给徒弟. 请用所学, 模拟这个知识点.
# 1. 定义师傅类Master.
class Master(object):
# 1.1 属性, kongfu = '[古法摊煎饼果子技术]'
def __init__(self):
self.kongfu = '[古法摊煎饼果子技术]'
# 1.2 行为, make_cake(), 表示: 摊煎饼.
def make_cake(self):
print(f'采用 {self.kongfu} 制作煎饼果子!')
# 2. 定义徒弟类Prentice, 继承自 师傅类.
class Prentice(Master):
pass
# 在main函数中测试.
if __name__ == '__main__':
# 3. 创建徒弟类对象.
p = Prentice()
# 4. 通过徒弟类对象, 调用 从师傅类(父类) 中继承过来的 属性 和 行为.
print(f'徒弟从师傅继承过来的属性: {p.kongfu}')
p.make_cake() # 徒弟从师傅继承过来的 行为.
2.3 多继承
多继承就是:一个类同时继承了多个父类。
语法:
class 类名(父类名1,父类名2,...): # 多个父类
代码
...
故事2:
小明是个爱学习的好孩子,想学习更多的摊煎饼果子技术,于是,在百度搜索到学校[school],报班来培训学习摊煎饼果子技术。
"""
多继承 细节:
1. Python中1个类可以继承多个父类, 写法格式为: class 子类名(父类1, 父类2...):
2. 如果1个类继承了多个父类, 则该子类可以拥有所有父类的 属性 和 行为. 前提: 父类的私有成员除外.
3. 如果1个类继承了多个父类, 且多个父类有同名的属性 和 行为, 优先参考 第1个父类的 内容, 这个是根据 MRO规则实现的.
4. MRO 全称是: Method Resolution Order, 方法解析顺序, 它规定了继承关系中, 属性和行为的 查找顺序, 即: 先找谁, 后找谁.
调用方式如下:
类名.__mro__ 属性的方式调用.
类名.mro() 行为(函数)的方式调用.
"""
# 故事2: 小明是个爱学习的好孩子, 想学更多的煎饼果子技术, 于是来到了学校, 报班学习摊煎饼果子技术. 请用所学, 模拟这个知识点.
# 1. 定义师傅类Master.
class Master(object):
# 1.1 属性, kongfu = '[古法摊煎饼果子技术]'
def __init__(self):
self.kongfu = '[古法摊煎饼果子技术]'
# 1.2 行为, make_cake(), 表示: 摊煎饼.
def make_cake(self):
print(f'采用 {self.kongfu} 制作煎饼果子!')
# 2. 定义学校类School.
class School(object):
# 2.1 属性, kongfu = '[摊煎饼果子技术]'
def __init__(self):
self.kongfu = '[摊煎饼果子技术]'
# 2.2 行为, make_cake(), 表示: 摊煎饼.
def make_cake(self):
print(f'采用 {self.kongfu} 制作煎饼果子!')
# 3. 定义徒弟类Prentice, 继承自 师傅类.
class Prentice(School, Master): # 多继承, 同名属性和行为, 优先参考第1个父类, 即: 从左往右的顺序.
pass
# 在main函数中测试.
if __name__ == '__main__':
# 4. 创建徒弟类对象.
p = Prentice()
# 5. 打印 徒弟类对象 从父类继承过来的 属性.
print(p.kongfu)
# 6. 打印 徒弟类对象 从父类继承过来的 行为.
p.make_cake()
# 7. 查看MRO规则, method Resolution Order, 方法解析顺序.
print(Prentice.__mro__)
print(Prentice.mro())
子类调用父类的同名方法顺序问题
当一个类有多个父类时,默认使用第一个父类的同名属性和方法,可以使用类名**.mro属性**或类名.mro()方法查看调用的先后顺序。
注:MRO(Method Resolution Order):方法解析顺序
print(Tudi.__mro__) print(Tudi.mro())
2.4 子类重写父类同名方法和属性
重写也叫作覆盖,就是当子类属性或方法与父类的属性或方法名字相同的时候,从父类继承下来的成员可以重新定义!
故事3:
fangge掌握了老师傅的技术后,自己潜心钻研出一套自己的独门配方的全新摊煎饼果子技术。 -老师傅!
"""
方法重写 解释:
概述:
子类中出现和父类一模一样的 属性 或者 行为(函数)时, 称为: 方法重写.
应用场景:
当子类需要从父类沿袭一些功能, 但是功能主体又有自己独有需求的时候, 就可以考虑使用方法重写了.
"""
# 故事3: 小明掌握了老师傅和学校的技术后, 自己钻研出一套 自己的 独门配方摊煎饼果子法. 请用所学, 模拟这个知识点.
# 1. 定义师傅类Master.
class Master(object):
# 1.1 属性, kongfu = '[古法摊煎饼果子技术]'
def __init__(self):
self.kongfu = '[古法摊煎饼果子技术]'
# 1.2 行为, make_cake(), 表示: 摊煎饼.
def make_cake(self):
print(f'采用 {self.kongfu} 制作煎饼果子!')
# 2. 定义学校类School.
class School(object):
# 2.1 属性, kongfu = '[摊煎饼果子技术]'
def __init__(self):
self.kongfu = '[摊煎饼果子技术]'
# 2.2 行为, make_cake(), 表示: 摊煎饼.
def make_cake(self):
print(f'采用 {self.kongfu} 制作煎饼果子!')
# 3. 定义徒弟类Prentice, 继承自 师傅类.
class Prentice(School, Master): # 多继承, 同名属性和行为, 优先参考第1个父类, 即: 从左往右的顺序.
# 3.1 属性
def __init__(self):
self.kongfu = '[独创(自研) 摊煎饼果子技术]'
# 3.2 行为, make_cake(), 表示: 摊煎饼.
def make_cake(self):
print(f'采用 {self.kongfu} 制作煎饼果子!')
# 在main函数中测试.
if __name__ == '__main__':
# 4. 创建徒弟类对象.
p = Prentice()
# 5. 打印 徒弟类对象 从父类继承过来的 属性.
print(p.kongfu)
# 6. 打印 徒弟类对象 从父类继承过来的 行为.
p.make_cake()
# 7. 查看MRO规则, method Resolution Order, 方法解析顺序.
print(Prentice.__mro__)
print(Prentice.mro())
"""
回顾: 方法重写
概述:
子类出现和父类一模一样的方法时, 称为: 方法重写.
应用场景:
子类需要沿袭父类的功能, 但是功能主体又有自己独有需求的时候, 就可以考虑使用方法重写了.
"""
# 需求: 定义手机类, 有打电话的功能. 定义新式手机类, 继承手机类, 重写 打电话的行为.
# 1. 定义手机类.
class Phone(object):
# 打电话的行为
def call(self, name):
print(f'----- 拨号: {name} 的手机号')
print('(拨号方)手机将 信号包发送到 (拨号方)就近的 基站')
print('基站解析信号包, 获取目标方的位置')
print('基站通过地下电缆将信号包 发送到 目标方就近的基站')
print('目标方就近的基站 将信号包发送到 目标方的手机上')
print('盲音等待, 嘟嘟嘟...')
# 2. 定义新式手机类.
class NewPhone(Phone):
# 打电话的行为
def call(self, name):
# 沿袭父类的功能.
super().call(name)
# 功能主体又有自己独有需求的时候.
print('新式手机的性能, 播放彩铃, 求佛...')
# main方法中完成测试.
if __name__ == '__main__':
# 3. 创建手机类对象, 打电话.
p = Phone()
p.call('刘亦菲')
print('*' * 20)
# 4. 创建新手机类对象, 打电话.
np = NewPhone()
np.call('胡歌')
2.5 子类调用父类方法
子类中仍想要保留父类的行为,则需要在子类中调用父类方法**.可以**直接使用父类名来进行调用,使用的方法:
父类名.父类方法名()
故事4:
很多顾客都希望能吃到徒弟做出的有自己独立品牌的煎饼果子,也有学校配方技术的煎饼果子味道。
"""
案例: 演示 方法重写后, 子类如何调用父类的 行为(函数)
问题: 重写后, 子类 如何访问 父类的成员?
答案:
方式1: 父类名.父类方法名(self) # self本类当前对象的引用.
方式2: super().父类方法名()
super 关键字介绍:
概述:
它代表 本类当前对象 父类的引用.
简单理解:
self 代表自己, super 代表父类.
细节:
1. super()只能初始化第1个父类的成员, 所以 super写法 不适用于 多继承, 更适用于 单继承.
2. 在单继承关系中, 可以把 super().父类方法名(self) 简写成 super().父类方法名()
3. 多继承关系中, 如果想精准的初始化某个父类的成员, 要通过 父类名.父类方法名(self) 的方式实现.
"""
# 故事4: 很多顾客都希望吃到 徒弟自研的煎饼果子, 也有 学校配方的煎饼果子味道. 请用所学, 模拟这个知识点.
# 1. 定义师傅类Master.
class Master(object):
# 1.1 属性, kongfu = '[古法摊煎饼果子技术]'
def __init__(self):
self.kongfu = '[古法摊煎饼果子技术]'
# 1.2 行为, make_cake(), 表示: 摊煎饼.
def make_cake(self):
print(f'采用 {self.kongfu} 制作煎饼果子!')
# 2. 定义学校类School.
class School(object):
# 2.1 属性, kongfu = '[摊煎饼果子技术]'
def __init__(self):
self.kongfu = '[摊煎饼果子技术]'
# 2.2 行为, make_cake(), 表示: 摊煎饼.
def make_cake(self):
print(f'采用 {self.kongfu} 制作煎饼果子!')
# 3. 定义徒弟类Prentice, 继承自 师傅类.
class Prentice(School, Master): # 多继承, 同名属性和行为, 优先参考第1个父类, 即: 从左往右的顺序.
# 3.1 属性
def __init__(self):
self.kongfu = '[独创(自研) 摊煎饼果子技术]'
# 3.2 行为, make_cake(), 表示: 摊煎饼.
def make_cake(self):
print(f'采用 {self.kongfu} 制作煎饼果子!')
# 3.3 行为, make_master_cake(), 从老师傅继承过来的 煎饼果子配方.
def make_master_cake(self):
# 子类调用父类行为, 方式1: 父类名.父类方法名(self)
# 初始化父类的 属性, 即: 调用父类的 __init__()函数
Master.__init__(self) # 等价于做了: self.kongfu = '[古法摊煎饼果子技术]'
Master.make_cake(self) # 调用父类的行为.
# 3.4 行为, make_school_cake(), 从学校继承过来的 煎饼果子配方.
def make_school_cake(self):
# 子类调用父类行为, 方式1: 父类名.父类方法名(self)
# 初始化父类的 属性, 即: 调用父类的 __init__()函数
School.__init__(self) # 等价于做了: self.kongfu = '[摊煎饼果子技术]'
School.make_cake(self) # 调用父类的行为.
# 3.5 行为, make_old_cake(), 从 父类继承过来的 煎饼果子配方.
def make_old_cake(self):
# 子类调用父类行为, 方式2: super().父类方法名()
super().__init__() # 初始化父类的成员.
super().make_cake()
# 在main函数中测试.
if __name__ == '__main__':
# 4. 创建徒弟类对象.
p = Prentice()
# 自研的.
p.make_cake() # 自研
# 5. 打印 徒弟类对象 从 Master(老师父类)继承过来的 行为.
# p.make_master_cake() # 古法
# 6. 打印 徒弟类对象 从 School(学校类)继承过来的 行为.
# p.make_school_cake() # 学校
# 7. 打印 徒弟类对象 从 父类继承过来的 行为. 即: 旧的煎饼果子配方.
p.make_old_cake() #
使用父类名进行初始化之后在调用相应的方法
2.6 super方法调用父类方法
使用super()调用父类方法,使用的方法:
super().父类方法名()
故事4:
很多顾客都希望能吃到徒弟做出的有自己独立品牌的煎饼果子,也有学校配方技术的煎饼果子味道。
# 1.定义师傅类
class Master(object):
# 1.1 属性
def __init__(self):
self.kofu = '[传统方法]'
# 1.2 方法
def make_cake(self):
print(f'使用{self.kofu}摊煎饼')
# 2.定义学校类
class School(object):
# 1.1 属性
def __init__(self):
self.kofu = '[方法]'
# 1.2 方法
def make_cake(self):
print(f'使用{self.kofu}摊煎饼')
# 3.定义徒弟类
class Tudi(Heima):
# 1.1 属性
def __init__(self):
self.kofu = '[独创技术]'
# 1.2 方法
def make_cake(self):
print(f'使用{self.kofu}摊煎饼')
# 1.4 使用学校技术
def schol_make_cake(self):
super().__init__()
super().make_cake()
# 3.实例化
xiaoming = Tudi()
print(xiaoming.kofu)
xiaoming.make_cake()
# xiaoming.master_make_cake()
xiaoming.schol_make_cake()
# print(Tudi.__mro__)
# print(Tudi.mro())
注意:使⽤super() 可以⾃动查找⽗类,适合单继承使⽤,多继承不建议使用
2.7 多层继承
故事5:
N年后,小明老了,想要把"有自己的独立品牌,也有学校配方技术的煎饼果子味道"的所有技术传授给自己的徒弟。
"""
案例: 演示 多层继承
问题: 重写后, 子类 如何访问 父类的成员?
答案:
方式1: 父类名.父类方法名(self) # self本类当前对象的引用.
方式2: super().父类方法名()
细节:
1. 你要分清楚 多层继承 和 多继承.
2. 多继承: 1个类有多个父类.
3. 多层继承: 继承的传递性.
"""
# 故事4: N年后, 小明老了, 想把自己独创的, 学校配方, 古法配方的煎饼果子技术, 都传给自己的徒弟. 请用所学, 模拟这个知识点.
# 1. 定义师傅类Master.
class Master(object):
# 1.1 属性, kongfu = '[古法摊煎饼果子技术]'
def __init__(self):
self.kongfu = '[古法摊煎饼果子技术]'
# 1.2 行为, make_cake(), 表示: 摊煎饼.
def make_cake(self):
print(f'采用 {self.kongfu} 制作煎饼果子!')
# 2. 定义学校类School.
class School(object):
# 2.1 属性, kongfu = '[摊煎饼果子技术]'
def __init__(self):
self.kongfu = '[摊煎饼果子技术]'
# 2.2 行为, make_cake(), 表示: 摊煎饼.
def make_cake(self):
print(f'采用 {self.kongfu} 制作煎饼果子!')
# 3. 定义徒弟类Prentice, 继承自 师傅类.
class Prentice(School, Master): # 多继承, 同名属性和行为, 优先参考第1个父类, 即: 从左往右的顺序.
# 3.1 属性
def __init__(self):
self.kongfu = '[独创(自研) 摊煎饼果子技术]'
# 3.2 行为, make_cake(), 表示: 摊煎饼.
def make_cake(self):
print(f'采用 {self.kongfu} 制作煎饼果子!')
# 3.3 行为, make_master_cake(), 从老师傅继承过来的 煎饼果子配方.
def make_master_cake(self):
# 子类调用父类行为, 方式1: 父类名.父类方法名(self)
# 初始化父类的 属性, 即: 调用父类的 __init__()函数
Master.__init__(self) # 等价于做了: self.kongfu = '[古法摊煎饼果子技术]'
Master.make_cake(self) # 调用父类的行为.
# 3.4 行为, make_school_cake(), 从学校继承过来的 煎饼果子配方.
def make_school_cake(self):
# 子类调用父类行为, 方式1: 父类名.父类方法名(self)
# 初始化父类的 属性, 即: 调用父类的 __init__()函数
School.__init__(self) # 等价于做了: self.kongfu = '[摊煎饼果子技术]'
School.make_cake(self) # 调用父类的行为.
# 3.5 行为, make_old_cake(), 从 父类继承过来的 煎饼果子配方.
def make_old_cake(self):
# 子类调用父类行为, 方式2: super().父类方法名()
super().__init__() # 初始化父类的成员.
super().make_cake()
# 4. 定义徒孙类, 继承子: 徒弟类.
class TuSun(Prentice): # 继承关系: TuSun -> Prentice -> School, Master -> object
pass
# 在main函数中测试.
if __name__ == '__main__':
# 4. 创建 徒孙类 对象, 调用: 从父类, 父类的父类 中继承过来的内容.
ts = TuSun()
ts.make_cake() # 自研
ts.make_school_cake() #
ts.make_master_cake() # 古法
-
继承:子类拥有父类的属性和方法
-
单继承:一个子类有一个父类
-
多继承:一个子类有多个父类
-
mro资源顺序
子类.mro() 子类.__mro__ 尝试去追踪底层代码 -
子类重新父类的属性和方法
-
优先使用子类的属性方法
-
-
需要显示的调用父类的属性和方法
-
父类.方法名(self)
-
Super()适合单继承
-
Super()按照mro资源表,去查找父类的调用顺序;不是按照我们的人为的继承
-
-
3 python中的封装
3.1 什么是封装
在软件编程中,将属性和方法书写到类的里面的操作即为封装,封装可以为属性和方法添加私有权限
3.2 定义和获取私有属性
在Python中,可以为属性设置私有权限,即设置某个属性不继承给子类。
设置私有权限的方式:在属性名前面加上两个下划线 __,格式:
self.__属性名
故事6:
fangge把技术传承给徒弟的同时,不想把自己的私房钱($5000000)继承给徒弟,这时就要为钱这个属性设置私有权限。
"""
封装 解释:
概述:
在Python中, 我们定义函数的动作, 叫: 封装.
在Python中, 我们定义类, 类中有属性 和 行为, 这个动作: 也叫封装.
专业版话述:
封装就是隐藏对象的 属性 和 实现细节, 仅对外提供公共的访问方式.
问1: 怎么隐藏?
答案: 私有化的方式, __ 注意, 这里是 两个下划线.
问2: 公共的访问方式是什么?
答案: 就是自己提供的对外访问内部成员的接口(方式), 例如: get_xxx(), set_xxx()
问3: 目前我们所学的内容, 子类可以从父类继承过来所有的 属性 和 行为, 那如果父类的某些 属性和行为不让子类继承, 如何实现呢?
答案: 可以通过 私有化解决.
私有化介绍:
概述:
被它修饰的内容, 只能在 本类中 直接访问.
格式:
__属性名 注意: 这里是 两个 _
__函数名()
特点:
私有化成员, 子类无法继承, 只能本类使用. 如果子类想访问, 必须通过提供的 公共的访问方式(接口)
"""
# 故事6: 小明把技术传给徒弟的同时, 不想把自己的私房钱继承给徒弟, 请用所学, 模拟该知识.
# 1. 定义 徒弟类.
class Prentice(object):
# 1.1 初始化属性.
def __init__(self):
self.kongfu = '[自研 摊煎饼果子技术]'
self.__money = 500 # 徒弟的私房钱, 私有化了.
# 1.2 行为, 摊煎饼.
def make_cake(self):
print(f'采用 {self.kongfu} 制作煎饼!')
# 1.3 定义公共的访问方式(接口), 查看 私房钱.
def get_money(self):
return self.__money
# 1.4 定义公共的访问方式(接口), 修改 私房钱.
def set_money(self, money):
self.__money = money
# 2. 定义 徒孙类, 继承子 徒弟类.
class TuSun(Prentice):
pass
# main方法中测试.
if __name__ == '__main__':
# 3. 创建 徒孙类对象.
ts = TuSun()
# 4. 尝试访问 从父类(徒弟类)中继承过来的内容.
print(ts.kongfu)
ts.make_cake()
# 5. 尝试访问 父类(徒弟类)的私房钱.
# print(f'(徒孙) 看到了 (徒弟)的私房钱: {ts.money}')
# print(f'(徒孙) 看到了 (徒弟)的私房钱: {ts.__money}')
print(f'(徒孙) 看到了 (徒弟)的私房钱: {ts.get_money()}')
# 修改 徒弟的私房钱.
ts.set_money(666666666)
print(f'(徒孙) 看到了 (徒弟)的私房钱: {ts.get_money()}')
3.3 定义和获取私有方法
在Python中,可以为方法设置私有权限,即设置某个方法不继承给子类。
设置私有权限的方式:在方法名前面加上两个下划线 __,格式:
def __方法名(self): ...
故事7:
小明把煎饼果子技术传承给徒弟的同时,不想把自己的独创配方制作过程继承给徒弟,这时就要为制作独创配方这个方法设置私有权限。
"""
封装 解释:
概述:
在Python中, 我们定义函数的动作, 叫: 封装.
在Python中, 我们定义类, 类中有属性 和 行为, 这个动作: 也叫封装.
专业版话述:
封装就是隐藏对象的 属性 和 实现细节, 仅对外提供公共的访问方式.
问1: 怎么隐藏?
答案: 私有化的方式, __ 注意, 这里是 两个下划线.
问2: 公共的访问方式是什么?
答案: 就是自己提供的对外访问内部成员的接口(方式), 例如: get_xxx(), set_xxx()
问3: 目前我们所学的内容, 子类可以从父类继承过来所有的 属性 和 行为, 那如果父类的某些 属性和行为不让子类继承, 如何实现呢?
答案: 可以通过 私有化解决.
私有化介绍:
概述:
被它修饰的内容, 只能在 本类中 直接访问.
格式:
__属性名 注意: 这里是 两个 _
__函数名()
特点:
私有化成员, 子类无法继承, 只能本类使用. 如果子类想访问, 必须通过提供的 公共的访问方式(接口)
"""
# 故事7: 小明把煎饼果子技术传承给徒弟的同时, 不想把具体的制作过程继承给徒弟, 请用所学, 模拟该知识.
# 1. 定义徒弟类.
class Prentice(object):
# 1.1 初始化属性.
def __init__(self):
self.kongfu = '[自研摊煎饼果子技术]'
self.__money = 500 # 私有化属性
# 1.2 行为, 摊煎饼. 私有方法, 子类无法继承.
def __make_cake(self):
print(f'采用 {self.kongfu} 摊煎饼!')
# 1.3 提供针对于 __money 私有属性的, 公共的访问方式.
def get_money(self):
return self.__money
def set_money(self, money):
self.__money = money
# 1.4 提供个公共的函数, 用于访问: 摊煎饼 这个私有的方法.
def make(self):
self.__make_cake()
# 2. 定义徒孙类.
class TuSun(Prentice):
pass
# 在main方法中测试.
if __name__ == '__main__':
# 3. 创建徒孙类对象.
ts = TuSun()
# 4. 尝试访问从父类继承过来的内容.
print(ts.kongfu) # [自研摊煎饼果子技术], 非私有属性.
# print(ts.money) # 报错.
print(f'徒弟的私房钱为: {ts.get_money()}')
# 修改私房钱.
ts.set_money(1000000)
print(f'徒弟的私房钱为: {ts.get_money()}')
print('-' * 20)
# 5. 尝试调用从父类继承过来的方法.
# ts.make_cake() 报错, 父类私有函数不能直接访问.
ts.make()
私有权限 :
-
私有属性
-
只能在类内部访问
-
定义公有方法获取get_xxx或设置set_xxx私有属性值
-
-
私有方法
-
只能在类内部访问
-
定义公有方法调用私有方法
-
4 python中的多态
4.1 多态定义
多态,指的是:多种状态。比如:同样一个函数在不同的场景下有不同的状态。
"""
多态介绍:
概述:
多态指的是同一个事物在不同场景下表现出来的不同形态, 状态.
Python中的多态指的是, 同一个函数, 传入不同的对象, 会实现不同的结果.
多态的前提条件:
1. 要有继承关系.
2. 要有方法重写.
3. 要有父类引用指向子类对象. an:Animal = Dog() an:Animal = Cat() 狗是动物, 猫是动物
好处:
提高代码的可维护性. 实现: 1个函数, 多种效果.
应用场景:
父类型充当函数形参的类型, 这样可以接受其任意的子类对象, 实现: 传入什么(子类)对象, 就调用其对应的功能.
细节:
有人说, Python中的多态其实是"伪多态", 因为 函数的形参类型, 并不能严格限制必须传入该类型或者其子类型的对象,
其实传入其它类型的对象也是可以的.
"""
# 案例: 动物类案例.
# 1. 定义动物类, 有 speak()函数, 表示: 叫.
class Animal(object):
def speak(self):
print('动物会叫!')
# 2. 定义狗类, 继承自动物类, 重写: speak()函数.
class Dog(Animal):
def speak(self):
print('狗 汪汪汪 叫!')
# 3. 定义猫类, 继承自动物类, 重写: speak()函数.
class Cat(Animal):
def speak(self):
print('猫 喵喵喵 叫!')
# 扩展: 定义1个手机类.
class Phone(object):
def speak(self):
print('我像只鱼儿在你的荷塘, 只为寻找那...')
# 4. 定义函数 make_noise(动物类对象), 接收动物对象, 实现: 传入什么动物, 就怎么叫.
def make_noise(an: Animal): # an:Animal 意思是: an "必须" 是Animal类的对象 或者 其子类对象.
# 接收动物对象, 实现: 传入什么动物, 就有: 对应的叫声.
an.speak() # 猫对象.speak(), 狗对象.speak()
# 在main方法中测试.
if __name__ == '__main__':
# 5. 分别创建猫类, 狗类对象.
c = Cat()
d = Dog()
# 6. 调用 make_noise()函数, 实现: 多态. 传入狗对象, 就: 汪汪汪叫, 传入猫对象, 就: 喵喵喵叫.
make_noise(c) # 猫 喵喵喵 叫!
make_noise(d) # 猫 喵喵喵 叫!
print('-' * 20)
# 7. 演示 "伪多态"
p = Phone()
make_noise(p)
# 扩展: 看变量属于什么类型.
print(type(p)) # type()函数, 看对象属于什么类型.
print(isinstance(p, Phone)) # isinstance(对象名, 类型) 判断对象是否是对应的类型.
print(isinstance(p, Animal)) # isinstance(对象名, 类型) 判断对象是否是对应的类型.
print(isinstance(c, Animal)) # True
4.2 多态的条件
1、有继承 (定义父类、定义子类,子类继承父类) 2、函数重写 (子类重写父类的函数) 3、父类引用指向子类对象 (子类对象传给父类对象调用者)
"""
需求:
构建对象对战平台object_play
1 英雄一代战机(战斗力60)与敌军战机(战斗力70)对抗。英雄1代战机失败!
2 卧薪尝胆,英雄二代战机(战斗力80)出场!,战胜敌军战机!
3 对象对战平台object_play, 代码不发生变化的情况下, 完成多次战斗.
分析:
抽象战机类 HeroFighter AdvHeroFighter;敌机EnemyFighter;
构建对象战斗平台, 使用多态实现
"""
# 1. 定义英雄1代机 战斗力为: 60
class HeroFighter(object):
def power(self):
return 60 # 攻击力: 60
# 2. 定义英雄2代机 战斗力为: 80, 继承自: 英雄1代机.
class AdvHeroFighter(HeroFighter): # advanced: 进阶...
def power(self):
return 80 # 攻击力: 80
# 3. 定义敌机, 战斗力为: 70
class EnemyFighter(object):
def power(self):
return 70 # 攻击力: 70
# 4. 构建对象对战平台 object_play(英雄机, 敌机)
def object_play(hf: HeroFighter, ef: EnemyFighter):
"""
构建对象对战平台, 模拟: 英雄机 和 敌机对战.
:param hf: 英雄机
:param ef: 敌机
:return: 无
"""
if hf.power() >= ef.power():
print('英雄机 获胜!')
else:
print('敌机 获胜!')
# 在main方法中测试.
if __name__ == '__main__':
# 5. 分别创建英雄1代机, 2代机, 敌机对象.
hf = HeroFighter()
hf2 = AdvHeroFighter()
ef = EnemyFighter()
# 6. 具体的对战过程.
# 英雄1代机 和 敌机
object_play(hf, ef) # 敌机 获胜!
# 英雄2代机 和 敌机
object_play(hf2, ef) # 英雄机 获胜!
4.3 多态的好处
1、在不改变框架代码的情况下,通过多态语法轻松的实现模块和模块之间的解耦合;实现了软件系统的可拓展
2、对解耦合的大白话解释:搭建的平台函数def object_play(herofighter:HeroFighter, enemyfighter:EnemyFighter) 相当于任务的调用者;子类、孙子类重写父类的函数,相当于子任务;相当于任务的调用者和任务的编写者进行了解耦合
3、对可拓展的大白话解释: 搭建的平台函数def object_play(herofighter:HeroFighter, enemyfighter:EnemyFighter),在不做任何修改的情况下,可以调用后来人写的代码
4、对“继承和多态对照理解”大白话解释:
继承相当于:孩子可以复用老爹的东西。
多态相当于:老爹框架,不做任何修改的情况下,可以可拓展的使用后来人(孩子)写的东西。
4.4 抽象类(接口)
"""
抽象类介绍:
概述:
在Python中, 抽象类也叫接口, 指的是: 有抽象方法的类, 就叫抽象类.
抽象方法:
没有方法体的方法, 即: 空实现的方法, 用pass修饰, 就叫: 抽象方法.
例如:
def get_sum():
pass
用法:
抽象类一般充当 父类, 即: 制定整个继承体系的 标准(规范)
具体的体现, 实现交由 子类来完成.
"""
# 需求: 定义空调类(AC), 制定标准: 制冷, 制热, 左右摆风. 两个厂商(美的, 格力)根据标准, 制作空调.
# 1. 定义空调类, 制定标准: 制冷, 制热, 左右摆风
class AC(object):
# 抽象方法.
# 制冷
def cool_wind(self):
pass
# 制热
def hot_wind(self):
pass
# 左右摆风
def swing_l_r(self):
pass
# 2. 美的空调.
class Media(AC):
# 制冷
def cool_wind(self):
print('美的空调 核心制冷技术 制作冷风')
# 制热
def hot_wind(self):
print('美的空调 核心制热技术 制作热风')
# 左右摆风
def swing_l_r(self):
print('美的空调 遥控器设置 左右摆风')
# 3. 格力空调.
class Gree(AC):
# 制冷
def cool_wind(self):
print('格力空调 核心制冷技术 制作冷风')
# 制热
def hot_wind(self):
print('格力空调 太阳能技术 制作热风')
# 左右摆风
def swing_l_r(self):
print('格力空调 AI控制 左右摆风')
# 测试代码
if __name__ == '__main__':
# 4. 测试 美的空调.
m = Media()
m.cool_wind()
m.hot_wind()
m.swing_l_r()
print('-' * 20)
# 5. 测试 格力空调.
g = Gree()
g.cool_wind()
g.hot_wind()
g.swing_l_r()
5 面向对象的其他特性
5.1 属性
对于属性,调用时有两种方式,如下:
self.对象属性名 # 类内部
或:
对象名.对象属性名 # 类外部
例如,编写一个手机类,有品牌[类内部]、颜色[类外部]属性。
# 1.定义类
class Phone(object):
def __init__(self):
self.brand = '华为'
# 2.实例化对象
phone = Phone()
phone.color = 'purple'
print(phone.brand)
print(phone.color)
5.2 类属性
所谓类属性,指的就是类所拥有的属性,它被共享于整个类中(即都可以直接调用),格式如下:
类名.类属性名 # 推荐使用
或:
对象名.类属性名
例如,在People类中定义一个名为count的类属性。
# 1.定义类
class Person(object):
count = 1
__height = 165
# 2.获取属性值:类名.类属性
print(Person.count)
# p = Person()
# print(p.count)
"""
属性 介绍:
概述:
属性 = 名词, 就是描述事物的外在特征的, 例如: 姓名, 年龄, 性别...
分类:
对象属性: 属于每个对象的属性, 即: A对象修改了他的属性值, 不会影响B对象的属性值.
类属性: 它是属于该类的所有对象 所共享的, 即: A对象修改了类属性, 则B对象用的是 修改后的属性值.
对象属性 相关格式:
定义格式:
类外: 对象名.属性名 = 属性值
类内: self.属性名 = 属性值
获取属性值的格式:
类外: 对象名.属性名
类内: self.属性名
类属性:
定义格式:
定义在类中, 函数外的 变量, 就叫: 类变量, 它能被该类下所有的对象所共享.
调用格式:
方式1: 类名.类属性名 # 推荐使用.
方式2: 对象名.类属性名 # 可以用, 但是不推荐.
细节: 什么时候定义类变量, 什么时候定义对象变量?
如果1个变量是被该类下所有的对象所共享的, 就考虑定义成: 类变量(类属性), 否则定义成: 对象变量(对象属性).
"""
# 需求: 定义学生类, 有 name属性, teacher_name属性, 创建学生类对象, 并测试. 请用所学, 模拟该知识.
# 1. 定义学生类.
class Student(object):
# 1.1 老师名字.
teacher_name = '夯哥' # 类属性, 每个对象所共享.
# 1.2 name属性, 每个学生的名字都不一样, 所以定义成: 对象属性.
def __init__(self):
self.name = '张三' # 对象属性.
# 在main方法中调用
if __name__ == '__main__':
# 2. 创建学生类对象.
s1 = Student()
s2 = Student()
# 3. 打印对象属性值.
print(f's1对象的 对象属性: {s1.name}') # '张三'
print(f's2对象的 对象属性: {s2.name}') # '张三'
# 4. 修改s1的对象属性值.
s1.name = '王五'
# 5. 重新打印对象属性值.
print(f's1对象的 对象属性: {s1.name}') # '王五'
print(f's2对象的 对象属性: {s2.name}') # '张三'
# 6. 打印 类属性值.
print(f's1对象的 类属性: {s1.teacher_name}') # '夯哥'
print(f's2对象的 类属性: {s2.teacher_name}') # '夯哥'
# 7. 修改s1的类属性值.
Student.teacher_name = '梅婷'
# 8. 重新打印 类属性值.
print(f's1对象的 类属性: {s1.teacher_name}') # '梅婷'
print(f's2对象的 类属性: {s2.teacher_name}') # '梅婷'
# 9. 类属性, 推荐使用 类名.的方式调用.
print(Student.teacher_name)
5.3 类方法
所谓类方法,指的是类所拥有的方法,并需要使用[装饰器]@classmethod来标识其为类方法
同时一定要注意的是对于类方法的第一个参数必须是类对象,通常以cls作为第一个参数名,格式如下:
@classmethod
def 类方法名(cls):
...
调用时,格式如下:
类名.类方法名() # 推荐使用
或:
对象名.类方法名()
例如,狗狗都喜欢吃骨头。
# 1.定义类
class Dog(object):
__num = 6
# 类方法
@classmethod
def eat(cls):
print(cls.__num)
print('小狗喜欢吃骨头')
# 2.调用类方法
Dog.eat()
# dog = Dog()
# dog.eat()
扩展:语法
类方法一般和类属性配合使用,尤其是使用私有类属性时。
6.4 静态方法
静态方法需要通过装饰器@staticmethod来来标识其为静态方法,且静态方法不需要多定义参数,
格式如下:
@staticmethod
def 静态方法名():
...
调用时,格式如下:
类名.静态方法名 # 推荐使用
或:
对象名.静态方法名
"""
类方法 和 静态方法详解:
概述:
类方法:
1. 第1个参数必须是 当前类的对象, 一般用 cls当做变量名(即: class)
2. 类方法必须通过 @classmethod 来修饰.
3. 类方法是属于 类的方法, 能被该类下所有的对象所共享.
4. 可以通过 类名. 或者 对象名. 的方式调用, 推荐: 前者.
静态方法:
1. 静态方法没有参数的硬性要求, 可以1个参数都不传.
2. 静态方法必须通过 @staticmethod 来修饰.
3. 类方法是属于 类的方法, 能被该类下所有的对象所共享.
4. 可以通过 类名. 或者 对象名. 的方式调用, 推荐: 前者.
区别:
类方法 和 静态方法的区别: 要不要传参, 即: 第一个参数是写 还是 不写, 再简单点说: 是否需要使用 该类的对象, 用就定义成 类方法, 不用就定义成 静态方法.
细节:
"""
# 需求: 定义学生类, 有 类属性 teacher_name, 对象属性: name. 类方法, 静态方法, 在测试类中进行测试.
# 1. 定义学生类, 有 类属性 teacher_name, 对象属性: name. 类方法, 静态方法
class Student(object):
# 1.1 老师名字.
teacher_name = '夯哥' # 类属性, 每个对象所共享.
# 1.2 name属性, 每个学生的名字都不一样, 所以定义成: 对象属性.
def __init__(self):
self.name = '张三' # 对象属性.
# 1.3 定义静态方法, 访问: teacher_name 这个类变量.
@staticmethod
def method01():
print(Student.teacher_name) # 类名. 的方式, 访问: 类变量.
# 1.4 定义类方法, 访问: teacher_name 这个类变量.
@classmethod
def method02(cls): # 这里的cls 是 class的意思.
print(cls.teacher_name) # 类名. 的方式, 访问: 类变量.
if __name__ == '__main__':
# 2. 创建学生对象.
s = Student()
s.method01() # 对象名. 的方式 访问静态方法, 可以, 但是不推荐.
Student.method01() # 类名. 的方式, 访问静态方法, 推荐.
Student.method02() # 类名. 的方式, 访问静态方法, 推荐.
【上一篇】Python进阶】8.面向对象基础
【下一篇】【Python进阶】10.深拷贝&浅拷贝
更多推荐


所有评论(0)