第七章 · 面向对象

继承与多态

本节目标

学完这一节,你会知道:

  1. 什么是继承,为什么它能减少重复代码
  2. 子类如何获得父类的属性和方法
  3. 如何重写父类方法
  4. super() 用来做什么
  5. 什么是多态

继承可以理解成:先写一个通用版本,再基于它写更具体的版本。

先跑一个例子

新建文件 inheritance_demo.py,写入:

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

    def speak(self):
        print(f"{self.name} 发出了声音")


class Dog(Animal):
    def speak(self):
        print(f"{self.name}:汪汪!")


class Cat(Animal):
    def speak(self):
        print(f"{self.name}:喵喵!")


animals = [Dog("旺财"), Cat("咪咪")]

for animal in animals:
    animal.speak()

运行:

python3 inheritance_demo.py

你会看到:

旺财:汪汪!
咪咪:喵喵!

DogCat 都继承了 Animal,但各自有不同的 speak()

什么是继承?

继承语法:

class 子类(父类):
    pass

例子:

class Animal:
    def eat(self):
        print("正在吃东西")


class Dog(Animal):
    pass


dog = Dog()
dog.eat()

Dog 没有写 eat(),但它继承了 Animaleat()

重写方法

如果子类想要自己的行为,可以重写父类方法。

class Animal:
    def speak(self):
        print("发出了声音")


class Dog(Animal):
    def speak(self):
        print("汪汪")

调用 Dog().speak() 时,会使用 Dog 自己的版本。

super()

子类经常需要先使用父类的初始化逻辑,再增加自己的属性。

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary


class Manager(Employee):
    def __init__(self, name, salary, department):
        super().__init__(name, salary)
        self.department = department

super().__init__(name, salary) 表示调用父类的 __init__()

完整例子:

manager = Manager("马哥", 30000, "技术部")

print(manager.name)
print(manager.salary)
print(manager.department)

多态

多态的意思是:不同对象可以用同一个方法名,但表现不同。

animals = [Dog("旺财"), Cat("咪咪")]

for animal in animals:
    animal.speak()

循环里不需要判断它是狗还是猫,只要它有 speak() 方法就行。

这会让代码更灵活。

逐行拆解

再看开头的例子:

class Dog(Animal):

定义 Dog 类,并继承 Animal

def speak(self):
    print(f"{self.name}:汪汪!")

重写父类的 speak() 方法。

for animal in animals:
    animal.speak()

同样调用 speak(),不同对象执行自己的版本。

自己改一改

inheritance_demo.py 改成通知系统:

class Notification:
    def __init__(self, title):
        self.title = title

    def send(self):
        print(f"发送通知:{self.title}")


class EmailNotification(Notification):
    def send(self):
        print(f"发送邮件:{self.title}")


class SMSNotification(Notification):
    def send(self):
        print(f"发送短信:{self.title}")


notifications = [
    EmailNotification("新评论"),
    SMSNotification("验证码"),
]

for notification in notifications:
    notification.send()

然后继续改:

  1. 增加一个 PushNotification
  2. 给父类增加 message 属性
  3. 子类用 super() 调用父类初始化

实战:支付方式

新建文件 payment.py

class Payment:
    def __init__(self, name):
        self.name = name

    def pay(self, amount):
        print(f"{self.name} 支付 {amount:.2f} 元")


class WeChatPay(Payment):
    def __init__(self):
        super().__init__("微信")


class AliPay(Payment):
    def __init__(self):
        super().__init__("支付宝")


def checkout(payment, amount):
    payment.pay(amount)
    print("支付完成")


checkout(WeChatPay(), 99.9)
checkout(AliPay(), 199.5)

checkout() 不关心具体是哪种支付方式,只要对象有 pay() 方法。

了解即可:isinstance()

可以判断一个对象是不是某个类的实例。

dog = Dog("旺财")

print(isinstance(dog, Dog))
print(isinstance(dog, Animal))

子类对象也属于父类。

常见错误

1. 忘记调用 super()

class Manager(Employee):
    def __init__(self, name, salary, department):
        self.department = department

这样 namesalary 没有初始化。应该写:

super().__init__(name, salary)

2. 子类方法名拼错

如果父类方法叫 speak(),子类写成 speek(),就不是重写,而是新增了另一个方法。

3. 为了继承而继承

继承适合“子类是父类的一种”。比如狗是动物。

如果只是“包含关系”,比如学生有成绩,不一定要用继承。

4. 继承层级太深

继承超过两三层后,代码会变难懂。初学时尽量保持简单。

小练习

练习 1:车辆继承

定义 Vehicle 父类,再定义 CarBike 子类,重写 run() 方法。

练习 2:员工和经理

定义 Employee,包含姓名和工资。定义 Manager,增加部门属性,并使用 super()

练习 3:统一打印

创建多个对象放进列表,循环调用同一个方法,观察多态效果。

参考答案

练习 1:

class Vehicle:
    def run(self):
        print("交通工具在移动")


class Car(Vehicle):
    def run(self):
        print("汽车在行驶")


class Bike(Vehicle):
    def run(self):
        print("自行车在骑行")

练习 2:

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary


class Manager(Employee):
    def __init__(self, name, salary, department):
        super().__init__(name, salary)
        self.department = department

练习 3:

vehicles = [Car(), Bike()]

for vehicle in vehicles:
    vehicle.run()

小结

这一节你学会了:

  1. 子类可以继承父类的属性和方法
  2. 子类可以重写父类方法
  3. super() 可以调用父类方法
  4. 多态让不同对象用同一个接口表现出不同行为
  5. 继承适合表达“某某是一种某某”的关系

下一节我们会学习魔法方法。它能让你写的类支持 print()len()+ 等 Python 内置语法。

继承让代码少重复,多一点家族感

Animal、Dog、Cat 的例子看着简单,但它藏着很实用的复用思路。子类可以继承父类,也可以有自己的声音。马哥提醒你:继承适合“某某是一种某某”,别什么关系都硬认亲。

讨论 (0)

还没有评论,来抢沙发吧!