【Python基础】12.面向对象-继承

面向对象OOP

继承

面向对象的三大特性

  • 封装 根据 职责属性方法 封装到一个抽象的类中
  • 继承 实现代码的重用,相同的功能不需要重复编写
  • 多态
    1. 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度
    2. 子类可以重写父类的方法,完成对方法的重新实现,用以实现自己独有的功能

单继承

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)

扩展父类的方法

如果在开发中,子类实现的方法需要以父类实现方法的结果为基础,就可以使用扩展的方式

  1. 在子类中重写父类的方法
  2. 使用 super().父类方法来调用父类方法的执行
  3. 调用完成后,实现子类特有的代码
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) 会出现死循环

访问父类的私有属性和私有方法

  1. 子类对象不能在自己的方法内部,直接访问父类的私有属性或者调用父类的私有方法

修改赛亚人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'
  1. 子类对象可以使用父类的公有方法,间接访问父类的私有属性和私有方法

修改一个赛亚人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
  • 23
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值