面向对象OOP
继承
面向对象的三大特性
- 封装 根据 职责 将 属性 和 方法 封装到一个抽象的类中
- 继承 实现代码的重用,相同的功能不需要重复编写
- 多态
- 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度
- 子类可以重写父类的方法,完成对方法的重新实现,用以实现自己独有的功能
单继承
class 子类名(父类名):
pass #依据实际情况新增代码
通过小括号内增加父类名的方法,完成子类对父类属性和方法的继承
定义一个种族Race类
# 定义一个种族类名为Race具有如下属性:
# 姓名 (name) 字符串值
# 年龄 (age) 整数值
# 能力值 (ablity_val) 整数值
# 家人 (family) 字典值
# 师傅 (master) -> 字符串值
# 朋友 (friends) 字典值
# 具备有 补充 supply() 修行 training() 战斗 fighting()的方法
class Race:
def __init__(self):
self.name = "未知"
self.age = 0
self.ability_val = 0
self.family = {}
self.master = "未知"
self.friends = {}
def supply(self):
"""
补充一次,能力值增加10
"""
self.ability_val += 10
print("补充一次,能力值增加10,目前所剩战力值为:%d" % self.ability_val)
def training(self):
"""
修行一次,能力值增加100
:return:
"""
self.ability_val += 100
print("修行一次,能力值增加100,目前所剩战力值为:%d" % self.ability_val)
def fighting(self):
"""
战斗一次,能力值减少10
"""
self.ability_val -= 10
print("战斗一次,能力值减少10,目前所剩战力值为:%d" % self.ability_val)
定义一个赛亚人SaiYan类,继承Race类
# 定义一个赛亚人类名为SaiYan继承于Race类,具有如下属性:
# 姓名 (name) 字符串值
# 年龄 (age) 整数值
# 能力值 (ablity_val) 整数值
# 家人 (family) 字典值
# 师傅 (master) -> 字符串值
# 朋友 (friends) 字典值
# 技能池 (fighting_skills) 列表值
# 具备有 变成猿人 be_unstoppable()的方法
class SaiYan(Race):
def _init_(self):
self.fighting_skills = []
def be_unstoppable(self):
"""
变成猿人,能力值增加10倍
:return:
"""
self.ability_val *= 10
print("变成猿人,能力值增加10倍,目前所剩战力值为:%d" % self.ability_val)
创建sun_wu_kong对象并初始化,然后调用方法
sun_wu_kong = SaiYan()
sun_wu_kong.name = "孙悟空"
sun_wu_kong.age = 500
sun_wu_kong.ability_val = 1000
sun_wu_kong.family = {"Wife": "Chi-Chi", "First-Son": "Gohan", "Father": "Bardock", "Mother": "Gine",
"Second-Son": "Goten"}
sun_wu_kong.master = "Master Roshi"
sun_wu_kong.friends = {"Yamcha": "Yamcha", "Krillin": "Krillin", "Tien": "Tien", "Chiaotzu": "Chiaotzu",
"Piccolo": "Piccolo"}
sun_wu_kong.fighting_skills = ["Kamehameha", "Spirit Bomb", "Instant Transmission", "Dragon Fist"]
print(sun_wu_kong.fighting_skills)
sun_wu_kong.supply()
sun_wu_kong.training()
sun_wu_kong.fighting()
sun_wu_kong.be_unstoppable()
继承的专业术语
- SaiYan 是 Race 的子类,Race 是 SaiYan 的父类,SaiYan 从 Race 类继承
- SaiYan 是 Race 的派生类,Race 是 SaiYan 的基类,SaiYan 从 Race 类派生
继承的传递性
定义SaiYanAndEarthlings类,继承自SaiYan
class SaiYanAndEarthlings(SaiYan):
def __init__(self):
self.name = "未知"
self.age = 0
self.ability_val = 0
self.family = {}
self.master = "未知"
self.friends = {}
self.fighting_skills = []
def read_and_study(self):
"""
读书学习一次,能力值增加10
"""
self.ability_val += 10
print("读书学习一次,能力值增加10,目前所剩战力值为:%d" % self.ability_val)
使用SaiYanAndEarthlings类创建对象,并调用父类,父类的父类的方法
sun_wu_fan = SaiYanAndEarthlings()
sun_wu_fan.name = "孙悟饭"
sun_wu_fan.age = 20
sun_wu_fan.ability_val = 100
sun_wu_fan.family = {"Father": "Gohan", "Mother": "Videl", "Grandfather": "Goku", "Grandmother": "Chi-Chi"}
sun_wu_fan.master = "Piccolo"
sun_wu_fan.friends = {"Pan": "Pan", "Bulla": "Bulla", "Trunks": "Trunks", "Goten": "Goten", "Krillin": "Krillin"}
sun_wu_fan.fighting_skills = ["Masenko", "Kamehameha", "Special Beam Cannon", "Destructo Disc"]
sun_wu_fan.read_and_study()
sun_wu_fan.supply()
sun_wu_fan.training()
sun_wu_fan.fighting()
sun_wu_fan.be_unstoppable()
运行结果
读书学习一次,能力值增加10,目前所剩战力值为:110
补充一次,能力值增加10,目前所剩战力值为:120
修行一次,能力值增加100,目前所剩战力值为:220
战斗一次,能力值减少10,目前所剩战力值为:210
变成猿人,能力值增加10倍,目前所剩战力值为:2100
从上述例子可以看出:
子类拥有父类、父类的父类中封装的所有属性和方法
方法的重写
- 当父类 的方法不能满足子类的需求时,可以对方法进行重写(override)
- 分为两种情况
- 对父类方法进行完全改造
- 使用父类方法的功能,并在其基础上做扩展
覆盖父类的方法
如果在开发中,子类需要实现与父类不同的方法,可以使用覆盖的方式,在子类重新编写父类的实现
- 修改 SaiYanAndEarthlings类 ,重写 be_unstoppable方法
class SaiYanAndEarthlings(SaiYan):
def read_and_study(self):
"""
读书学习一次,能力值增加10
"""
self.ability_val += 10
print("读书学习一次,能力值增加10,目前所剩战力值为:%d" % self.ability_val)
def be_unstoppable(self):
"""
变成猿人,能力值增加10倍
:return:
"""
self.ability_val *= 100
print("变成猿人,能力值增加10倍,目前所剩战力值为:%d" % self.ability_val)
扩展父类的方法
如果在开发中,子类实现的方法需要以父类实现方法的结果为基础,就可以使用扩展的方式
- 在子类中重写父类的方法
- 使用 super().父类方法来调用父类方法的执行
- 调用完成后,实现子类特有的代码
class SaiYanAndEarthlings(SaiYan):
def __init__(self):
super(SaiYanAndEarthlings, self).__init__()
self.is_angry = False
def read_and_study(self):
"""
读书学习一次,能力值增加10
"""
self.ability_val += 10
print("读书学习一次,能力值增加10,目前所剩战力值为:%d" % self.ability_val)
def be_unstoppable(self):
"""
变成猿人,能力值增加10倍
:return:
"""
super().be_unstoppable()
if self.is_angry:
self.ability_val *= 10
print("愤怒状态下变成猿人,能力值又增加10倍,目前所剩战力值为:%d" % self.ability_val)
super关键字
- super在python中是一个特殊类
- super()就是用super类创建出来的对象
- 最常使用的场景就是在子类重写父类方法时,调用实现父类的方法
关于子类调用父类的方法
python2.x 中
- 可以使用
类名.方法名(self)
的方式调用,这种方法在Python3.x中仍然适用,但已不推荐适用 - 如果因为手误,使用
子类名.方法名(self)
会出现死循环
访问父类的私有属性和私有方法
- 子类对象不能在自己的方法内部,直接访问父类的私有属性或者调用父类的私有方法
修改赛亚人SaiYan类
class SaiYan(Race):
def _init_(self):
self.fighting_skills = []
def be_unstoppable(self):
"""
变成猿人,能力值增加10倍
:return:
"""
self.ability_val *= 10
print("变成猿人,能力值增加10倍,目前所剩战力值为:%d" % self.ability_val)
def __talk_with_dragon(self):
print("和神龙对话")
使用子类SaiYanAndEarthlings的对象调用父类的私有方法
sun_wu_fan._talk_with_dragon()
结果会报错
AttributeError: 'SaiYanAndEarthlings' object has no attribute '_talk_with_dragon'
- 子类对象可以使用父类的公有方法,间接访问父类的私有属性和私有方法
修改一个赛亚人SaiYan类
class SaiYan(Race):
def __init__(self):
super().__init__()
self.__have_7_dragon_balls = None
def _init_(self):
super().__init__()
self.__have_7_dragon_balls = False
def __talk_with_dragon(self):
if self.__have_7_dragon_balls:
print("和神龙对话")
def give_7_dragon_balls(self):
self.__have_7_dragon_balls = True
def be_unstoppable(self):
"""
变成猿人,能力值增加10倍
:return:
"""
self.ability_val *= 10
print("变成猿人,能力值增加10倍,目前所剩战力值为:%d" % self.ability_val)
def auth_talk_with_dragon(self):
self.__talk_with_dragon()
使用子类SaiYanAndEarthlings的对象调用父类的私有方法
sun_wu_fan.give_7_dragon_balls()
sun_wu_fan.auth_talk_with_dragon()
结果
和神龙对话
多继承
- 子类可以拥有多个父类,并且所有父类的属性和方法都可以被继承
- 语法类似于继承,中间使用","隔开所有父类
定义一个新的Earthlings类
class Earthlings(Race):
def __init__(self):
super().__init__()
self.can_cook = False
def cook(self):
if self.can_cook:
print(self.name+"会煮饭")
else:
print(self.name+"不会煮饭")
修改SaiYanAndEarthlings类,继承SaiYan类和Earthlings类
class SaiYanAndEarthlings(SaiYan, Earthlings):
def __init__(self):
super(SaiYanAndEarthlings, self).__init__()
self.is_angry = False
def read_and_study(self):
"""
读书学习一次,能力值增加10
"""
self.ability_val += 10
print("读书学习一次,能力值增加10,目前所剩战力值为:%d" % self.ability_val)
def be_unstoppable(self):
"""
变成猿人,能力值增加10倍
:return:
"""
super().be_unstoppable()
if self.is_angry:
self.ability_val *= 10
print("愤怒状态下变成猿人,能力值又增加10倍,目前所剩战力值为:%d" % self.ability_val)
在SaiYanAndEarthlings的对象中使用Earthlings类的方法
sun_wu_fan = SaiYanAndEarthlings()
sun_wu_fan.name = "孙悟饭"
sun_wu_fan.age = 20
sun_wu_fan.ability_val = 100
sun_wu_fan.family = {"Father": "Gohan", "Mother": "Videl", "Grandfather": "Goku", "Grandmother": "Chi-Chi"}
sun_wu_fan.master = "Piccolo"
sun_wu_fan.friends = {"Pan": "Pan", "Bulla": "Bulla", "Trunks": "Trunks", "Goten": "Goten", "Krillin": "Krillin"}
sun_wu_fan.fighting_skills = ["Masenko", "Kamehameha", "Special Beam Cannon", "Destructo Disc"]
sun_wu_fan.can_cook = True
sun_wu_fan.cook()
运行结果
孙悟饭会煮饭
注意事项
- 如果父类之间存在同名属性或者方法,应该避免使用多继承,防止不清方法的类时哪一个
- 在代码运行过程中,python不会报错,因为有MRO方法解决顺序机制
使用__mro__属性查看方法的执行顺序
- python 对类提供内置属性__mro__,可以查看方法的顺序
- MRO(method resolution order),主要用于 在多继承时判断方法、属性和调用路径
print(SaiYanAndEarthlings.__mro__)
结果如下:
(<class '__main__.SaiYanAndEarthlings'>, <class '__main__.SaiYan'>, <class '__main__.Earthlings'>, <class '__main__.Race'>, <class 'object'>)
- 在搜索方法时,是按照__mro__的输出结果从左往右的顺序查找的
- 如果在当前类的中找到方法,就执行,不再搜索
- 如果最后一个类都没有对应方法,程序报错
新式类和旧式类
object 类是Python 为所有对象提供的基类,提供有一些内置的属性和方法,可以使用dir函数查看
- 新式类:以 object 为基类的类,推荐使用
- 旧式类:不以 object 为基类的类,不推荐使用
- 在python3.x环境下,定义类时,如果不指定基类为object,则会默认使用object类为当前类的基类
- 在python2.x环境下,定义类时,如果不指定基类为object,则不使用object类为当前类的基类
新式类和旧式类在多继承时,会影响到执行方法的搜索顺序
所以在定义类时,如果没有父类,建议统一显式定义其继承自object
class 类名(object):
pass