python 基础系列(五) — Python中的继承

python基础系列索引
python 基础系列(一) — Python介绍
python 基础系列(二) — Python基本语法
python 基础系列(三) — Python中的变量进阶
python 基础系列(四) — Python中的面向对象
python 基础系列(五) — Python中的继承
python 基础系列(六) — Python的异常及其处理
python 基础系列(七) — Python中的模块
python 基础系列(八) — Python中的文件操作
python 基础系列(九) — Python中的vi – 终端中的编辑器
python 基础系列(十) — Windows CMD命令大全
python 基础系列(十一) — 使用PyCharm远程调试树莓派python代码 — Windows CMD命令大全
如果你对python感兴趣,不妨看一下我的其他文章
-
继承的分类
- 单继承
- 多继承
面向对象的三大特性
- 1.封装 根据 职责 将属性和方法封装到一个抽象类中
- 2.继承 实现代码的重用,相同的代码不需要重复的编写
- 3.多态 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度
继承的语法
class 类名(父类名):
pass
代码示例
class Animal:
def eat(self):
print("吃")
def drink(self):
print("喝")
def sleep(self):
print("睡觉")
class Dog(Animal):
def bark(self):
print("汪汪叫")
class Xiaotianquan(Dog):
def fly(self):
print("我是哮天犬,我会飞")
xiaotianquan = Xiaotianquan()
xiaotianquan.eat()
xiaotianquan.drink()
xiaotianquan.sleep()
xiaotianquan.bark()
xiaotianquan.fly()
运行结果
吃
喝
睡觉
汪汪叫
我是哮天犬,我会飞
继承中的方法重写
继承的优点
- 子类拥有父类全部的方法和属性
- 子类继承自父类,可以直接享用父类的方法,不用再次开发
重写父类方法的两种情况
- 覆盖父类的方法
- 对父类的方法进行扩展
覆盖父类的方法:
代码示例:
class Animal:
def eat(self):
print("吃")
def drink(self):
print("喝")
def sleep(self):
print("睡觉")
class Dog(Animal):
def bark(self):
print("汪汪叫")
class Xiaotianquan(Dog):
def fly(self):
print("我是哮天犬,我会飞")
def bark(self):
print("和人一样的叫...")
xiaotianquan = Xiaotianquan()
xiaotianquan.eat()
xiaotianquan.drink()
xiaotianquan.sleep()
xiaotianquan.bark()
xiaotianquan.fly()
执行结果
吃
喝
睡觉
和人一样的叫...
我是哮天犬,我会飞
-
扩展父类的方法,使用 super() 方法调用父类方法,当然我们也可以使用
父类名.方法名(self)
的形式进行调用,当然,self是必须传递的class Animal:
def eat(self):
print(“吃”)def drink(self): print("喝") def sleep(self): print("睡觉")
class Dog(Animal):
def bark(self):
print(“汪汪叫”)class Xiaotianquan(Dog):
def fly(self):
print(“我是哮天犬,我会飞”)
def bark(self):
print(“和人一样的叫…”)
# 使用 super() 方法调用父类方法
super()
print(“哮天犬的叫声”)xiaotianquan = Xiaotianquan()
xiaotianquan.eat()
xiaotianquan.drink()
xiaotianquan.sleep()
xiaotianquan.bark()
xiaotianquan.fly()
运行结果
吃
喝
睡觉
和人一样的叫...
哮天犬的叫声
我是哮天犬,我会飞
使用父类名的方式调用
示例代码
class Animal:
def eat(self):
print("吃")
def drink(self):
print("喝")
def sleep(self):
print("睡觉")
class Dog(Animal):
def bark(self):
print("汪汪叫")
class Xiaotianquan(Dog):
def fly(self):
print("我是哮天犬,我会飞")
def bark(self):
print("和人一样的叫...")
# `父类名.方法名(self)`的方式调用父类方法
Dog.bark(self)
print("哮天犬的叫声")
xiaotianquan = Xiaotianquan()
xiaotianquan.eat()
xiaotianquan.drink()
xiaotianquan.sleep()
xiaotianquan.bark()
xiaotianquan.fly()
运行结果:
吃
喝
睡觉
和人一样的叫...
汪汪叫
哮天犬的叫声
我是哮天犬,我会飞
注意:子类名调用会出现死循环
父类的私有属性和私有方法不可以被子类访问
代码示例
class Animal:
def eat(self):
self.__food = "实物"
print("吃")
def __drink(self):
print("喝")
def sleep(self):
print("睡觉")
class Xiaotianquan(Animal):
def fly(self):
print("我是哮天犬,我会飞")
print("获取事物%s" % self.__food)
self.__drink()
xiaotianquan = Xiaotianquan()
xiaotianquan.fly()
执行结果
Traceback (most recent call last):
File "E:/学习和总结/groovyStudy/python实战代码/newcode/venv/Include/soldiers_raided.py", line 88, in <module>
xiaotianquan.fly()
File "E:/学习和总结/groovyStudy/python实战代码/newcode/venv/Include/soldiers_raided.py", line 83, in fly
print("获取事物%s" % self.__food)
AttributeError: 'Xiaotianquan' object has no attribute '_Xiaotianquan__food'
python中的多继承
- 子类可以拥有刀哥父类,并且具有所有子类的属性和方法
- 如孩子可以继承爸爸和妈妈的所有属性
多继承语法
继承的语法
class 类名(父类名1,父类名2):
pass
多继承,注意避免父类出现同名属性或者同命方法
如果开发中父类存在同名的属性或者是方法,应当避免使用多继承,如果使用可,法法和属性的调用会直接引用依赖前面的对象的属性和方法
代码演示
class A:
def __init__(self):
self.name="A"
def test(self):
print(self.name)
print("A 的 test方法")
class B:
def __init__(self):
self.name = "B"
def test(self):
print(self.name)
print("B 的 test方法")
class C (B,A):
pass
c=C()
print(c.name)
c.test()
执行结果
B
B
B 的 test方法
python 中MRO – 方法搜索顺序
查看类中方法的的调用顺序
类名.__mro__
运行结果
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
从上面的结果可以看出 ,优先从当前类C中查找属性或者方法;如果没有找到就会从类B中查找,如果还是没有找到,如果,没有找到就会从类A中查找
面向对象中的多态
- 多态: 不同的子类对象调用相同的父类方法,产生不同的执行结果
- 多态 可以增加代码的灵活性
- 以 继承 和 重写父类的方法 为前提
- 是调用方法的技巧,不会影响到类的内部设计
重写父类的方法,可以使相同的方法得到不同的执行结果
代码示例
class Dog:
def __init__(self, name):
self.name = name
def game(self):
print("%s 蹦蹦跳跳的玩耍..." % self.name)
class XiaoTianQuan(Dog):
def game(self):
print("%s 飞到天上去玩耍..." % self.name)
class Person:
def __init__(self, name):
self.name = name
def game_with_dog(self, dog):
print("%s 和 %s 快乐的玩耍" % (self.name, dog.name))
dog.game()
# 创建一个狗对象
wangcai = Dog("旺财")
xiaotianquan=XiaoTianQuan("小权")
# 创建一个人对象
xiaoming = Person("小明")
# 人和狗一起玩耍
xiaoming.game_with_dog(wangcai)
xiaoming.game_with_dog(xiaotianquan)
运行结果
小明 和 旺财 快乐的玩耍
旺财 蹦蹦跳跳的玩耍...
小明 和 小权 快乐的玩耍
小权 飞到天上去玩耍...
实例
使用类模板创建出来的对象叫做示例
###类属性和实例属性 ###
- 类属性(类似java的类的静态成员变量),就是给类对象中定义的属性
- 通常用来记录
与这个类相关
的特征 - 类属性 不会用于记录具体对象的特征(换句话说,与具体的一个对象没有关系,和类有关系)
类属性的访问仅可以使用 类名.变量名
,无论是在类的内部还是在其他任意的位置,均可以使用上面的方式进行访问
代码示例
class Tool:
# 使用赋值与定义类属性,记录创建工具对象的总数
count = 0
def __init__(self, name):
self.name = name
# 每次创建对象针对类属性做一个加一的操作
Tool.count += 1
print("Tool.count = %s"% Tool.count)
# 创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("锄头")
tool3 = Tool("剪刀")
运行结果
Tool.count = 1
Tool.count = 2
Tool.count = 3
python中属性的获取机制
- 在python中 属性的获取 存在一个 向上的查找机制

- 因此访问类属性存在两种方式
- 1.类名.属性名
- 2.对象名.属性名(不推荐,如果存在对象属性名和类属性名称相同,会优先访问对象的属性名称)
注意
- 如果使用
对象.类属性 = 值
赋值只会给对象添加一个属性,而不会影响到类属性的值
优先级 方法属性 < 对象属性 < 类属性
使用对象.类属性赋值会出现错误,解释器会优先对对象的属性赋值
class Tool:
# 使用赋值与定义类属性,记录创建工具对象的总数
count = 0
def __init__(self, name):
self.name = name
# 每次创建对象针对类属性做一个加一的操作
Tool.count += 1
print("Tool.count = %s" % Tool.count)
# 创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("锄头")
tool3 = Tool("剪刀")
print("使用对象访问类属性 : count = %s" % tool1.count)
tool1.count = 99
print("修改后访问类属性的值 : count = %s" % Tool.count)
运行结果
Tool.count = 1
Tool.count = 2
Tool.count = 3
使用对象访问类属性 : count = 3
修改后访问类属性的值 : count = 3
类方法和静态方法
-
类属性 就是针对 类对象 定义的属性
- 使用赋值语句 在
class
关键字下方可以定义类属性 - 类属性用于记录
与这个类相关
的特征
- 使用赋值语句 在
-
类方法 就是针对类对象定义的方法
- 在 类方法 内部可以直接访问 类属性 或者调用 其他的类方法(类比java静态方法)
基本语法
@classmethod
def method(cls):
pass
-
类方法需要用
@classmethod
来标识,告诉解释器这是一个类方法 -
类方法的第一个参数 应该是一个
cls
- 有那个类调用的方法,方法内的
cls
那个类的引用 - 这个 参数和 实例方法 的第一个参数
self
类似 - 当然 类方法的参数使用其他变量名称也可以,不过习惯上使用
cls
- 有那个类调用的方法,方法内的
-
通过类名调用类方法时,无需传入
cls
,那个类调用cls
就会指向那个类 -
在方法的内部
- 可以通过
cls
,访问类的属性 - 也可以通过
cls
,调用其他的类方法
- 可以通过
代码示例
class Tool:
# 使用赋值与定义类属性,记录创建工具对象的总数
count = 0
def __init__(self, name):
self.name = name
# 每次创建对象针对类属性做一个加一的操作
Tool.count += 1
@classmethod
def show_tool_count(cls):
print("Tool.count = %s" % cls.count)
# 创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("锄头")
tool3 = Tool("剪刀")
Tool.show_tool_count()
运行结果
Tool.count = 3
什么时候使用静态方法
在开发中以下两种情况通常将方法定义为静态方法
-
既不需要访问 示例属性 或调用 实例方法
-
也 不需要访问 类属性 或调用 类方法
-
这个时候,可以把这个方法封装成一个 静态方法
语法如下
@staticmethod
def 静态方法名():
pass
- 静态方法需要使用
@staticmethod
来标识,告诉编译器这是个静态方法 - 通过 类名.静态方法名 调用静态方法
代码示例
class Dog:
@staticmethod
def show_msg():
print("这是一个小狗...")
Dog.show_msg()
运行结果
这是一个小狗...
小游戏案例
代码示例
class Game:
# 记录全场最高成绩
top_score = 0
def __init__(self, u_nme):
self.u_name = u_nme
@staticmethod
def show_help():
print("帮助信息。,让植物进入大门")
@classmethod
def show_top_score(cls):
print("您当前的最高成绩为 %.2f" % cls.top_score)
def start_game(self):
print("%s 开始游戏啦!"% self.u_name)
# 1.查看帮助信息
Game.show_help()
# 2.查看历史最高分
Game.show_top_score()
# 3.开始游戏
xiaoming = Game("小明")
xiaoming.start_game()
运行结果
帮助信息。,让植物进入大门
您当前的最高成绩为 0.00
小明 开始游戏啦!
总结 – 方法的类型如何选择:
- 1.示例方法 – 方法内部需要访问实例属性
- 内部可以使用
示例名.属性名
方式访问属性
- 内部可以使用
- 2.类方法 – 方法内部只需要访问类属性
- 3.静态方法 – 方法内部不需要访问实例属性和类属性
单例设计模式
- 目的 ,让类创建的对象,在系统中只有唯一的一个实例
- 每次执行
类名()
返回的对象,内存地址是相同的
复习前面几章我们知道,
对象创建过程,解释器底层实际上做了两件事情
-
调用
__new__
为需要创建的对象分配内存地址 -
调用类的
__init__
方法,为对象赋初值 -
__new__
方法是由Object
基类提供的内置静态方法,主要作用有两个:- 在内存中为对象分配内存空间
- 返回对象的引用
-
python的解释器获得对象的引用后,将引用作为第一个参数,传递给
__init__
方法
重写
___new__
方法的代码非常固定
- 重写
___new__
方法一定要return super().__new__(cls)
- 否则python解释器得不到分配了内存的对象的引用,就不会调用对象的初始化方法
- 注意:
___new__
是一个静态方法。在调用时需主动传递cls
参数
示例代码
class Game:
# 记录全场最高成绩
top_score = 0
def __new__(cls, *args, **kwargs):
print("为对象分配内存空间")
# 没有按照要求返回将会得不到对象地址的引用
return super().__new__(cls)
def __init__(self, u_nme):
self.u_name = u_nme
@staticmethod
def show_help():
print("帮助信息。,让植物进入大门")
@classmethod
def show_top_score(cls):
print("您当前的最高成绩为 %.2f" % cls.top_score)
def start_game(self):
print("%s 开始游戏啦!" % self.u_name)
# 1.查看帮助信息
Game.show_help()
# 2.查看历史最高分
Game.show_top_score()
# 3.开始游戏
xiaoming = Game("小明")
xiaoming.start_game()
运行结果
帮助信息。,让植物进入大门
您当前的最高成绩为 0.00
为对象分配内存空间
小明 开始游戏啦!
单例模式的使用
示例代码
class MusicPlayer:
instance =None
def __new__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance=super().__new__(cls)
return cls.instance
print(MusicPlayer())
print(MusicPlayer())
运行结果
<__main__.MusicPlayer object at 0x00000227022E7E80>
<__main__.MusicPlayer object at 0x00000227022E7E80>
解决单例中,初始化多次的问题
在类属性中增加一个 init_flag
变量作为初始化的标识,初始化成功后将这个标识置为true,后续不再初始化
代码示例
class MusicPlayer:
instance =None
init_flag=False
def __init__(self):
if MusicPlayer.init_flag :
return
# 执行初始化代码
print("这里是执行的一撮初始化代码")
# 将标识位置为True
MusicPlayer.init_flag=True
def __new__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance=super().__new__(cls)
return cls.instance
print(MusicPlayer())
print(MusicPlayer())
运行结果
这里是执行的一撮初始化代码
<__main__.MusicPlayer object at 0x00000172BE0E7E80>
<__main__.MusicPlayer object at 0x00000172BE0E7E80>