Python 面向对象:继承与多态

目录

一、通俗理解继承

二、自动复用父类属性、方法

三、方法重写:子类自定义专属行为

四、super() :子类重写构造方法

五、继承基础要点

六、多态:一套逻辑适配不同对象

七、综合示例:猫狗双子类对比

八、继承与多态适用场景

九、注意事项

常见错误与排查

十、本章小结


上一章我们掌握了 @classmethod 类方法,学会统一操作全类共享的数据。但在写代码的过程里,经常会出现多个模板重复写相同属性、相同行为的情况,比如小猫、小狗都属于动物,都拥有名字、毛色,也都会吃饭睡觉,如果分开写两套完整代码,会出现大量重复内容。

本章我们学习继承机制,复用已写好的模板代码,还能自定义差异化逻辑;最后理解多态,一套调用逻辑适配多种对象,大幅精简重复代码

一、通俗理解继承

我们用动物举例更好理解:

  • 父类 Animal:通用动物模板,存放所有动物通用特征(名字、毛色)和基础行为(吃饭、睡觉)。
  • 子类 Dog:小狗专属模板,不用重复写通用内容,直接沿用父类全部逻辑,只新增小狗独有的奔跑动作,还能改写吃饭的专属行为。

简单类比:父类是通用基础图纸,子类不需要从零绘制,直接复用图纸,再额外添加专属功能。

二、自动复用父类属性、方法

# 父类:通用动物模板
class Animal(object):
    def __init__(self, name, colour):
        self.name = name
        self.colour = colour

    def sleep(self):
        print(f"{self.colour}的{self.name}正在睡觉")

子类 Dog,括号写入父类名称,代表继承 Animal
class Dog(Animal):
# 子类新增独有的行为
    def run(self):
        print(f"{self.name}撒欢向前奔跑")

创建小狗实例
dog = Dog("大黄", "白色")
调用从父类继承的 sleep 方法
dog.sleep()
调用子类独有的 run 方法
dog.run()

运行输出:

白色的大黄正在睡觉
大黄撒欢向前奔跑

可以看到子类没有定义构造方法和 sleep,却能直接使用父类提前写好的全部逻辑。

三、方法重写:子类自定义专属行为

通用动物的吃饭逻辑比较笼统,小狗进食有专属行为,我们在子类定义和父类同名方法,就会覆盖原有逻辑,这个操作叫做方法重写。

class Animal(object):
    def __init__(self, name, colour):
        self.name = name
        self.colour = colour

    def eat(self):
        print("动物正在吃饭")

    def sleep(self):
        print(f"{self.colour}的{self.name}正在睡觉")

class Dog(Animal):
# 重写父类 eat 方法,替换专属逻辑
    def eat(self):
        print(f"{self.name}正在大口吃狗粮")

    def run(self):
        print(f"{self.name}撒欢向前奔跑")

dog = Dog("大黄", "白色")
dog.eat()
dog.sleep()
dog.run()

运行结果:

大黄正在大口吃狗粮
白色的大黄正在睡觉
大黄撒欢向前奔跑

规则:子类和父类方法名称完全相同时,调用会优先执行子类内部代码。这里要区分清楚:Python 不支持其他语言的方法重载(同名不同参数),我们讲的是方法重写(override)——子类定义和父类完全同名的方法,覆盖父类逻辑。

四、super() :子类重写构造方法

前面例子中,子类没有写自己的 __init__,直接复用父类构造。但如果子类自己写了 __init__,默认不会自动调用父类的 __init__,会拿不到父类属性,这是新手第一大坑。

解决办法:在子类 __init__ 中调用 super(),把父类构造方法也执行一遍,同时还能新增子类独有的属性。

class Animal(object):
    def __init__(self, name, colour):
        self.name = name
        self.colour = colour

    def sleep(self):
        print(f"{self.colour}的{self.name}正在睡觉")

class Dog(Animal):
    def __init__(self, name, colour, dog_type):
# 调用父类的构造方法,拿到 name、colour
        super().__init__(name, colour)
# 子类新增自己独有的属性
        self.dog_type = dog_type

    def run(self):
        print(f"{self.name}撒欢向前奔跑")

dog = Dog("大黄", "白色", "金毛")
dog.sleep()
dog.run()
print(dog.dog_type)

运行输出:

白色的大黄正在睡觉
大黄撒欢向前奔跑
金毛

一句话说明:当子类自己定义 __init__ 之后,不会自动继承父类构造,需要 super(),才能拿到父类定义的 name、colour。

为什么必须用 super()?子类一旦自己写了 __init__,就会完全覆盖父类的构造逻辑,父类里定义的 name、colour 都不会自动生成。super() 的作用就是先执行一遍父类的 __init__,把公共属性准备好,再继续添加子类独有的属性。如果不调用 super(),直接访问 self.name 会直接报错 AttributeError。

super() 的通用写法:super().__init__(父类需要的参数),把父类构造方法需要的参数原样传进去即可。子类新增的属性放在 super() 调用之后定义,逻辑更清晰。

极简对照示范:下面把错误写法和正确写法放在一起,一眼就能看懂差别。

错误写法(忘记调用 super())

class Dog(Animal):
    def __init__(self, name, colour, dog_type):
        # 没有调用 super().__init__,父类属性不会生成
        self.dog_type = dog_type
dog = Dog("大黄", "白色", "金毛")
print(dog.name)  # 报错:AttributeError

正确写法(调用 super())

class Dog(Animal):
    def __init__(self, name, colour, dog_type):
        # 先调用父类构造,拿到 name、colour
        super().__init__(name, colour)
        # 再新增子类独有属性
        self.dog_type = dog_type
dog = Dog("大黄", "白色", "金毛")
print(dog.name)      # 大黄
print(dog.dog_type)  # 金毛

一句话总结:子类自己写 __init__ 后,第一行先写 super().__init__(父类需要的参数),父类属性就都能拿到了。

五、继承基础要点

  • 继承写法:class 子类名(父类名),括号内填写需要复用的模板名称;
  • 子类自动拥有父类所有实例属性、通用方法;
  • 子类可以新增独有的方法,拓展专属功能;
  • 同名方法会发生重写,子类逻辑优先级更高;
  • Python 支持多层继承,子类可以继承另一子类的全部内容。
  • 单继承:class Dog(Animal),只继承一个父类,最常用;
  • 多继承:class Dog(Animal, RunAbility),同时继承多个父类,实际开发中优先使用单继承,多继承少用。

六、多态:一套逻辑适配不同对象

日常场景:人可以和不同小狗玩耍,不管小狗性格活泼还是安静,都不用单独写一套互动代码,只要小狗拥有相同的玩耍方法,就能统一调用,这就是多态的核心思路。

完整演示代码:

# 第一种小狗类型
class Dog1(object):
    def __init__(self, name):
        self.name = name

    def play(self):
        print(f"{self.name}疯跑打闹玩耍")

第二种小狗类型
class Dog2(object):
    def __init__(self, name):
        self.name = name

    def play(self):
        print(f"{self.name}安静慢慢玩")

人类模板,统一和小狗互动
class Person(object):
    def __init__(self, name):
        self.name = name

    def play_with_dog(self, dog):
        print(f"{self.name}和{dog.name}一起玩耍:")
        dog.play()

实例化不同小狗、人物
dog1 = Dog1("小黄")
dog2 = Dog2("小黑")
person = Person("小王")
同一套方法,传入不同对象自动执行对应逻辑
person.play_with_dog(dog1)
print("--------------------")
person.play_with_dog(dog2)

输出效果:

小王和小黄一起玩耍:
小黄疯跑打闹玩耍
--------------------
小王和小黑一起玩耍:
小黑安静慢慢玩

多态核心特点:

  • 多个不同类定义名称一致的方法;
  • 同一个函数可以接收任意这类对象,不需要区分对象具体类型;
  • 传入不同实例,会自动执行各自内部的逻辑,代码拓展性更强。

七、综合示例:猫狗双子类对比

我们新增小猫子类,直观对比继承与重写的使用效果:

class Animal(object):
    def __init__(self, name, colour):
        self.name = name
        self.colour = colour

    def eat(self):
        print("普通动物进食")

    def sleep(self):
        print(f"{self.colour}的{self.name}睡觉")

小狗子类
class Dog(Animal):
    def eat(self):
        print(f"{self.name}啃狗粮")

    def run(self):
        print(f"{self.name}快速奔跑")

小猫子类
class Cat(Animal):
    def eat(self):
        print(f"{self.name}舔猫粮")

    def climb(self):
        print(f"{self.name}爬上屋顶")

dog = Dog("大黄", "白色")
cat = Cat("小白", "橘色")
dog.eat()
dog.sleep()
dog.run()
print("================")
cat.eat()
cat.sleep()
cat.climb()

八、继承与多态适用场景

继承适用场景:多个类存在大量重复属性、通用行为,把公共内容抽取为父类,子类直接复用,减少重复书写,后续统一修改逻辑也只需要改动一处。

多态适用场景:需要一套统一调用逻辑兼容多种对象,只要求对象拥有同名方法,不用单独区分对象类型,提升代码通用性。

为了更直观地对比继承与多态的异同,下面用一张表格梳理两者的核心区别:

对比维度继承多态
核心目的复用父类已有的属性与方法,减少重复代码用一套统一调用逻辑适配多种对象,提升代码通用性
代码关系子类与父类存在明确的继承关系,通过 class 子类(父类) 建立不强制要求父子继承关系,只要对象拥有同名方法即可
使用场景多个类存在大量重复属性、通用行为时,抽取公共内容为父类需要一套调用逻辑兼容多种对象,且对象都具备同名方法时
实现方式子类直接继承父类,可新增专属方法,也可重写同名方法多个类定义名称一致的方法,由调用方统一传入不同实例
优点减少重复书写,后续统一修改逻辑只需改动父类一处调用方无需区分对象具体类型,代码拓展性更强

九、注意事项

  • 重写仅针对方法,不会改动父类原始代码;
  • 继承后可以直接使用父类全部属性,不用重复定义;
  • 多态不强制要求父子继承关系,只要方法名称一致就能实现;
  • Python 的多态属于鸭子类型:“长得像鸭子、叫得像鸭子,就当做鸭子”,不需要强制继承同一个父类,只要方法名相同就可以统一调用;
  • 子类可以自由新增专属属性、独有方法,不会影响父类。

常见错误与排查

在使用继承和 super() 时,最容易踩到下面几个坑,提前了解能少走很多弯路。

  • 忘记调用 super().__init__ 导致属性丢失:子类自己写了 __init__ 却没有调用 super().__init__,父类里定义的 name、colour 等属性就不会生成,访问 self.name 会直接报 AttributeError。解决办法:在子类 __init__ 第一行调用 super().__init__(父类需要的参数),把公共属性准备好。
  • 多继承时的 MRO 问题:多继承时方法查找顺序由 MRO(方法解析顺序)决定,如果多个父类有同名方法,调用顺序可能和预期不一致,甚至出现菱形继承的复杂情况。解决办法:优先使用单继承;确需多继承时,用 类名.__mro__ 查看解析顺序,并让父类也正确调用 super() 保证协作。
  • super() 参数传错或漏传:调用 super().__init__ 时没有把父类构造方法需要的参数原样传进去,会导致父类初始化失败或属性缺失。解决办法:仔细核对父类 __init__ 的参数列表,把需要的参数完整传入,子类新增属性放在 super() 调用之后定义。

十、本章小结

通过 class 子类(父类) 实现继承,子类复用父类全部属性与方法;子类可以新增专属方法,定义同名方法即可重写原有逻辑;多态依靠同名方法实现,同一套代码可适配多种对象;继承解决代码重复问题,多态提升代码兼容拓展能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值