六、Python基础(封装、继承、多态)
目录:
- 六、Python基础(封装、继承、多态)
-
-
-
- 一、继承与多态
-
- 1.单继承
- 2.方法的重写
-
- super().父类方法
-
-
-
- 3.父类的私有属性和私有方法
- 4.多继承
-
-
- print(子类.\_\_mro__)
-
-
-
- 5.多态
- 6.术语
- 二、类属性和方法
-
- 1.类属性
-
-
- 类名.类属性
- 实例名.类属性
-
-
-
- 2.@classmethod类方法
-
-
- 类名.类方法名(cls, 参数列表)
-
-
-
- 3.@staticmethod静态方法
-
-
- 类名.静态方法名(参数列表)
-
-
-
- 4.阶段小结:游戏计分板
- 5.补充:当方法要同时访问类属性和实例属性时
- 三、单例
-
- 1.单例设计模式
- 2.\_\_new__ 方法
- 3.Python中的单例
- 上一篇文章
- 下一篇文章
-
-
-
面向对象的三大属性:
- 封装 根据职责将属性和方法封装在一个个类中,并基于这些类来创建对象
- 继承 实现代码的复用,相同的代码不需要重复编写
- 多态 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度

文章图片
单继承的代码结构如图所示:

文章图片
- 子类继承拥有了父类的所有属性和方法,可以直接享用,无须再次开发
- 子类里可额外增加父类里没有的属性和方法
- 子类会全部继承父类的所有属性和方法,并且可以在 父——子 之间不断传递下去,即子类继承的属性和方法会越来越多,越来越强大——进化论
- Dog类 是 Animal类 的子类,Animal类 是 Dog类 的父类
- Dog类 是 Animal类 的派生类,Animal类 是 Dog类 的基类
class Animal:
def eat(self):
passdef run(self):
passdef drink(self):
passclass Dog(Animal):
def shout(self):
pass
2.方法的重写 (1)情况1:覆盖父类的方法
有时候,子类的某些属性或方法的实现可能并不想和父类相同,如子狗的毛色和父狗的毛色可能不一样,这个时候就需要用 方法的重写 来覆盖父类中的属性或方法,如图:
【Python基础|六、Python基础(封装、继承、多态)】覆盖方法:直接在子类中重新编写与 “需要覆盖的方法” 同名的方法即可

文章图片
在重写之后,在子类对象调用方法或属性时,只会调用子类重写后的方法(因为已覆盖)
例:
class Animal:
def eat(self):
passdef run(self):
passclass Dog(Animal):
def run(self):
pass
(2)情况2:对父类的方法进行扩展
对于父类的一个方法,若子类除 父类原有方法的实现 需要实现外,仍有另一部分也需要实现(而父类没有的那部分),则子类需要在父类的方法的基础上进行扩展
super().父类方法 可以在覆盖父类方法后的方法里,调用原父类方法,实现父类方法的再现我们只需先覆盖掉原父类的方法,写入需要扩展的代码;再在覆盖父类方法的基础上,在需要的位置利用 super().父类方法 调用回原父类方法(父类方法的再现)即可
例:
class Animal:
def eat(self):
passdef run(self):
passclass Dog(Animal):
def run(self):
"""另外补充需要扩展的代码即可"""
super().run()
"""另外补充需要扩展的代码即可"""
pass
3.父类的私有属性和私有方法
- 子类对象不能在自己的方法内部,直接访问父类的私有属性和私有方法
- 子类对象可以通过 父类的公有方法 间接访问到父类的私有属性和私有方法
class Animal:
"""定义属性"""
def __init__(self):
self.__name = "狗蛋"
self.__variety = "Huskie""""定义一个私有方法"""
def __run(self):
print("访问私有方法:这是一个父类的私有方法")"""定义一个访问父类私有属性的方法"""
def visit(self):
print("访问私有属性:__name:%s,__variety:%s" % (self.__name, self.__variety))
self.__run()class Dog(Animal):
def text(self):
self.visit()example = Dog()
example.text()

文章图片
4.多继承
- 子类可以拥有多个父类,并且具有所有父类的属性和方法

文章图片
多继承使用的注意事项:
- 如果不同的父类中存在同名的方法,子类对象在调用父类的方法时,会调用哪个父类的方法呢?
- 在开发中,应该尽量避免上述这种容易造成混淆的情况,如果父类之间存在同名的属性或方法时,应该尽量避免使用多继承
*对上述问题调用优先级的清晰解答——MRO方法搜索顺序(了解):
Python中针对类提供了一个内置属性 __mro__ 可以查看方法搜索顺序,主要用与在多继承时判断方法、属性的调用路径
print(子类.__mro__) 显示调用方法/属性时的顺序,从左到右显示的父类来顺序查找,找到即执行,找不到即向右查找5.多态 不同的子类对象调用相同的父类方法时,可以通过方法的重写和继承来产生不同的执行效果
其中,中,object类是所有类的基类
object类是一些对象通用的方法,如:字符串的方法…
在定义父类时,可能会看到有 class 父类(object): ,这是因为在Python3.X中,类默认是基于object的新类来定义的,在Python2.X中,默认用的是经典类,一般不推荐使用经典类,因此在创建父类时,可以加上(object)
在其他类创建的对象中调用多态类创建的对象时:其他类创建的对象调用多态类创建的对象,由于方法/属性的重写的继承,调用相同的方法可能产生不同的执行结果,形成多态
- 调用相同的方法可能产生不同的执行结果,形成多态(重要)

文章图片
class Transportation:
"""运输父类"""
def __init__(self, vehicle):
"""属性:交通工具名"""
self.vehicle = vehicledef arrive(self):
print("%s成功到达指定地点" % self.vehicle)class Plane(Transportation):
"""飞机子类,基于运输父类"""
def arrive(self):
"""方法的重写"""
print("%s以最快的速度到达指定地点" % self.vehicle)class Person:
"""人类"""
def __init__(self, name):
"""属性:人名"""
self.name = namedef choice(self, vehicle_choice):
"""选择的交通工具"""
vehicle_choice.arrive()"""波士顿:飞机"""
Boston = Plane("Boston")
"""泰坦尼克号:轮船"""
Titanic = Transportation("Titanic")
"""约翰:人"""
John = Person("John")"""约翰选择不同的交通工具,会带来不同的结果,形成多态"""
John.choice(Boston)
John.choice(Titanic)

文章图片
6.术语
- 基于类创建的对象通常也称为类的实例
- 创建对象的过程通常也称为实例化
- 对象的属性通常也称为实例属性
- 对象调用的方法通常也称为实例方法
二、类属性和方法 1.类属性 在创建类时,我们会用 __init__ 来定义对象的属性,但另外,类也有它的属性,类的属性不需要写在方法内,而直接写在类的内部(class定义类的下方)
类属性就是给类对象中定义的属性,通常用来记录与这个类相关的特征,类属性不会用于记录具体对象的特征
例:
class Tool:
"""使用赋值语句,定义类属性,记录创建工具对象的总数"""
count = 0def __init__(self, name):
self.name = name
"""每创建一个类,做一个计数"""
Tool.count += 1tool1 = Tool("锤子")
tool2 = Tool("扳手")
tool3 = Tool("斧头")print("可以用类名来访问类属性:%d" % Tool.count)
for temp_object in (tool1, tool2, tool3):
print("而且可以通过对象(实例)来访问类属性:%d" % temp_object.count)

文章图片
- 对于通过对象名来访问属性的——Python优先查找对象属性,若对象属性中不存在,则访问类属性(即可能存在对象属性和类属性同名的情况)
- 通过类名来访问属性的,则只会查找类属性,因此在查找类属性时,不推荐通过对象名来查找属性
类名.类属性 可以在类内部或外部访问到类属性注:赋值陷阱
实例名.类属性 也可以访问到类属性,但不推荐
在类的外部可以通过 对象.属性 = 属性值 会给对象定义一个对象属性
2.@classmethod类方法 类方法就是针对类的对象定义的方法
定义类方法的代码结构如图所示:

文章图片
- 类方法需要用 修饰器@classmethod 来标识,告诉解释器这是一个类方法
- 类方法的第一个参数应该是 cls
- 由哪一个类调用的方法,方法内的 cls 就是哪一个类的引用,与 self 是十分类似的
- cls 可以用其他名称代替,但是习惯上用 cls
类名.类方法名(cls, 参数列表) 可以调用类方法例:
注:cls 不传递参数
class Tool:
"""使用赋值语句,定义类属性,记录创建工具对象的总数"""
count = 0"""定义一个类方法,用于打印工具的数量count"""
@classmethod
def show_tool_count(cls):
print("工具对象的总数为:%d" % cls.count)def __init__(self, name):
self.name = name
"""每创建一个类,做一个计数"""
Tool.count += 1tool1 = Tool("锤子")
tool2 = Tool("扳手")
tool3 = Tool("斧头")Tool.show_tool_count()

文章图片
3.@staticmethod静态方法 在开发时,如果需要在类中封装一个方法,这个方法:
- 既不需要访问实例属性或者调用实例方法
- 也不需要访问类属性或者调用类方法
定义静态方法的代码结构如图所示:

文章图片
- 静态方法需要用 修饰器@staticmethod 来标识,告诉解释器这是一个静态方法
类名.静态方法名(参数列表) 可以通过调用静态方法4.阶段小结:游戏计分板 需求:
- 设计一个Game类
- 其中包含两个属性:
定义一个类属性:top_score 记录游戏的历史最高分
定义一个实例属性:player_name 记录当前游戏的玩家姓名 - 其中包含三个方法:
定义一个静态方法:show_help 显示游戏帮助信息
定义一个类方法:show_top_score 显示历史最高分
实例方法:start_game 开始当前玩家的游戏 - 主程序步骤:
(1)查看游戏帮助信息
(2)查看游戏历史最高分——最高分玩家
(3)创建游戏对象,开始游戏
class Game(object):
"""Game类""""""类属性:历史最高分"""
top_score = 0
"""类属性:最高分玩家"""
top_player: str = Nonedef __init__(self, player_name):
"""实例属性:当前游戏的玩家名"""
self.player_name = player_name@staticmethod
def show_help():
print("--这是游戏帮助信息--")@classmethod
def show_top_score(cls):
print("历史最高分:%d,玩家:[%s]" % (cls.top_score, cls.top_player))def start_game(self):
print("游戏开始")
temp_score = int(input("玩家[%s]的最终分数是:"))
if temp_score >= Game.top_score:
print("恭喜[%s],游戏分数:%d,突破游戏记录" % (self.player_name, temp_score))
Game.top_score = temp_score
Game.top_player = self.player_nameelse:
print("[%s]的最终得分是:%d" % (self.player_name, temp_score))"""调用类方法无需创建对象"""
Game.show_help()
Game.show_top_score()lcx = Game("~宪宪")
lcx.start_game()
lcx.show_top_score()

文章图片
5.补充:当方法要同时访问类属性和实例属性时 如果方法内部既需要访问类属性,又要访问实例属性时,这个方法应该定义成实例方法
- 实例方法 方法内部需要访问实例属性,也可以访问类属性
- 类方法 方法内部只需要访问类属性
- 静态方法 方法内部不需要访问类属性和实例属性
使用设计模式是为了可复用代码、让代码易读性更高、保证代码的可靠性
单例设计模式:
- 目的 让类创建的对象,在系统中只有唯一的一个实例
- 每一次执行 类名() 返回的对象,内存地址是相同的
- 音乐播放对象:同时只能播放一首音乐(一个实例/对象)
- 回收站对象:所有被清理的软件都被放入同一回收站(一个实例/对象)
- 打印机对象:打印机只能同时打印一份文件(一个实例/对象)
2.__new__ 方法
- 使用 类名() 创建对象时,Python解释器会首先调用 __new__ 方法为对象分配内存空间
- __new__ 方法是一个由 object 基类提供的内置静态方法,主要作用有两个:
在内存中为对象分配空间
返回对象的引用 - 重写 __new__ 方法的代码十分固定
- 重写 __new__ 方法一定要 return super().__new__(cls),否则Python解释器得不到分配了空间的对象引用
- super() 可以访问父类,因此 super().__new__(cls) 实际上是访问基类的 __new__ 方法,用于分配一片内存空间
- __new__ 是一个静态方法,在调用时需要主动传递 cls 参数
例:
class MusicPlayer(object):
def __new__(cls, *args, **kwargs):
print("创建对象,分配空间")return super().__new__(cls)def __init__(self):
print("音乐播放器初始化")"""创建播放器对象"""
player = MusicPlayer()
print(player)

文章图片
3.Python中的单例 让类创建的对象,在系统中只有唯一的一个实例:
- 重写 __new__ 方法 ,其中:
如果类属性 is None,调用父类方法分配空间,并在类属性中获得返回结果 - 返回类属性中记录的对象引用

文章图片
(图摘自黑马程序员)
提示:如果已经首次创建过实例了,则上述类属性就不为None了,因此只要不重新分配空间,instance 就一直是首次创建实例时的引用,直接返回 instance,即直接返回首次创建实例时的引用,那么每次创建对象时的地址都是唯一的,实现单例
例:
class MusicPlayer(object):
"""音乐播放器""""""记录实例的引用情况"""
instance = Nonedef __new__(cls, *args, **kwargs):
"""如果没有实例,就调用父类方法分配空间"""
if cls.instance is None:
cls.instance = super().__new__(cls)
"""如果已有实例,就分配内存,直接返回原有实例,实现地址唯一"""return cls.instancedef __init__(self):
print("音乐播放器初始化")"""创建多个对象验证地址是否相同"""
player1 = MusicPlayer()
print(player1)
player2 = MusicPlayer()
print(player2)

文章图片

文章图片
冗余性解决:既然单例场景中创建的对象都是同一个实例,那么如果每次创建一个实例,类中每次都会自动进行一次 __init__ 初始化动作,造成冗余,那么有没有一种办法,可以让初始化动作只被执行一次呢?
解决方法:
- 定义一个类属性 init_flag,标记是否执行过初始化动作,初始值为 False
- 在 __init__ 方法中,判断 init_flag 是否为 False,是则执行初始化动作,然后将 init_flag 设置为 True,限制下一次初始化动作,这样,当下一次再创建实例时,就不会再执行初始化动作了
class MusicPlayer(object):
"""音乐播放器""""""记录实例的引用情况"""
instance = None
"""记录是否执行过初始化方法"""
init_flag = Falsedef __new__(cls, *args, **kwargs):
"""如果没有实例,就调用父类方法分配空间"""
if cls.instance is None:
cls.instance = super().__new__(cls)
"""如果已有实例,就分配内存,直接返回原有实例,实现地址唯一"""return cls.instancedef __init__(self):
if not MusicPlayer.init_flag:
print("执行初始化动作")
MusicPlayer.init_flag = True"""创建多个对象验证地址是否相同"""
player1 = MusicPlayer()
player2 = MusicPlayer()
player3 = MusicPlayer()
于是,我们即使创建了多个实例, __init__ 初始化动作也只被执行一次了

文章图片
上一篇文章
- 五、Python基础(类与对象)
推荐阅读
- Python|百度飞桨领航团零基础Python速成营课程总结
- paddle课程|【paddle领航团基础python课程】三岁水课—结营大作业
- python|python 计算器 casio_Python编程之计算器/字符及界面
- python学习|python应用学习系列笔记
- python学习|python应用学习(一)——python生成二维码
- python|Pandas实例|药品发放汇总与excel表数据回填
- python|pyopengl全解析-4
- python|python 测试用例 自动生成_pythonpytest自动测试框架生成测试报告,PythonPytest,自动化...
- python|sqlmap使用