[Python 基础课程]继承

在 Python 的面向对象编程(OOP)中,继承(Inheritance) 是一种重要的机制,它允许一个类(称为子类派生类 )从另一个类(称为父类基类超类 )中继承属性和方法。

为什么需要继承

考虑我们之前定义的 Person 类,现在我们想创建 StudentTeacher 类。它们都属于"人",所以都有姓名和年龄。

python 复制代码
# 传统的做法,没有继承
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def introduce(self):
        print(f"大家好,我叫 {self.name},今年 {self.age} 岁。")

class Student:
    def __init__(self, name, age, student_id):
        # 代码重复
        self.name = name
        # 代码重复
        self.age = age 
        self.student_id = student_id

    def introduce(self):
        # 代码重复
        print(f"大家好,我叫 {self.name},今年 {self.age} 岁。")
        print(f"我的学号是 {self.student_id}。")

class Teacher:
    def __init__(self, name, age, subject):
        # 代码重复
        self.name = name
        # 代码重复
        self.age = age 
        self.subject = subject

    def introduce(self):
        # 代码重复
        print(f"大家好,我叫 {self.name},今年 {self.age} 岁。")
        print(f"我教 {self.subject}。")

你会发现 nameage 属性以及 introduce 方法在 PersonStudentTeacher 类中重复出现了。这种代码重复会导致:

  • 维护困难: 如果你需要修改"人"的共同行为(比如 introduce 方法),你必须在所有相关的类中都修改一遍。
  • 扩展性差: 每增加一个新的"人"的角色(比如 Employee),你都要重新编写这些通用部分。

继承就是为了解决这些问题: 它允许你定义一个通用的父类 ,把共同的属性和方法放在父类中。然后,特定的子类 可以继承这些共同部分,并添加自己独有的属性和方法,或者修改(重写)父类的行为。

继承的基本用法

在 Python 中,子类在定义时,在类名后面的括号中指定它所继承的父类。

python 复制代码
class ChildClass(ParentClass, OtherParentClass):

示例:PersonStudentTeacher 的继承关系

python 复制代码
class Person:
    species = "人类" # 类属性

    def __init__(self, name, age):
        self.name = name
        self.age = age
        print(f"创建了一个 Person 对象: {self.name}")

    def introduce(self):
        print(f"大家好,我叫 {self.name},今年 {self.age} 岁,是 {self.species}。")

    def celebrate_birthday(self):
        self.age += 1
        print(f"{self.name} 过生日了,现在 {self.age} 岁了!")

# Student 继承自 Person
class Student(Person):
    def __init__(self, name, age, student_id):
        # 调用父类 (Person) 的构造器来初始化共同属性
        super().__init__(name, age)
        # Student 独有的属性
        self.student_id = student_id
        print(f"创建了一个 Student 对象: {self.name}, 学号: {self.student_id}")

    # 重写 (Override) 父类的 introduce 方法
    def introduce(self):
        # 调用父类的 introduce 方法,重用其逻辑
        super().introduce()
        print(f"我的学号是 {self.student_id}。")

    def study(self):
        # Student 独有的方法
        print(f"{self.name} 正在努力学习。")

# Teacher 继承自 Person
class Teacher(Person):
    def __init__(self, name, age, subject):
        # 调用父类 (Person) 的构造器
        super().__init__(name, age)
        # Teacher 独有的属性
        self.subject = subject 
        print(f"创建了一个 Teacher 对象: {self.name}, 教授: {self.subject}")

    # 重写父类的 introduce 方法
    def introduce(self):
        # 重用父类的 introduce 逻辑
        super().introduce()
        print(f"我教 {self.subject}。")

    def teach(self):
        # Teacher 独有的方法
        print(f"{self.name} 正在教 {self.subject}。")

# --- 演示与测试 ---
print("--- 创建对象 ---")
person = Person("王五", 40)
student = Student("小明", 18, "S12345")
teacher = Teacher("李老师", 35, "数学")

print("\n--- 调用方法 ---")
person.introduce()
person.celebrate_birthday()

student.introduce()
student.study()
# 继承自 Person
student.celebrate_birthday()

teacher.introduce()
teacher.teach()
# 继承自 Person
teacher.celebrate_birthday() 

在 Python 中,子类默认会继承父类的所有属性和方法。如果你想让父类的某个属性不被继承,可以通过使用 __ 前缀创建私有属性。

python 复制代码
class Parent:
    # 这是一个私有属性,子类无法直接访问
    __private_property = "这是父类的私有属性"

    def __init__(self):
        # 这是一个私有实例属性
        self.__private_instance_property = "这是父类的私有实例属性"

    def get_private_property(self):
        # 通过方法返回私有属性
        print(self.__private_property)
        print(self.__private_instance_property)

class Child(Parent):
    def __init__(self):
        # 调用父类的构造函数
        super().__init__()
        print("尝试从子类访问父类的私有属性:")
        try:
            # 尝试访问父类的私有属性,会引发 AttributeError
            print(self.__private_instance_property)
        except AttributeError as e:
            print(f"  错误:{e}")

# 创建子类实例
child_obj = Child()
# 尝试从外部访问父类的私有属性,也会引发 AttributeError
try:
    print(child_obj.__private_instance_property)
except AttributeError as e:
    print(f"  外部访问错误:{e}")

print("\n通过父类方法访问:")
# 通过父类的方法可以成功访问
child_obj.get_private_property()

super() 函数

在子类中,我们经常需要调用父类的方法,特别是父类的构造器 __init__()。这时,我们使用内置的 super() 函数。

  • super().__init__(...) 调用父类的 __init__ 方法来初始化父类定义的属性。这是一种推荐的做法,确保父类的初始化逻辑得到执行。
  • super().method_name(...) 调用父类的其他方法。这在子类重写了父类方法后,仍想在子类中执行父类的原始逻辑时非常有用。

:::warning

在 Python 3 中,super() 函数不带参数就可以自动获取当前类和实例,所以直接写 super().__init__(...) 即可。在 Python 2 中,可能需要写成 super(ChildClass, self).__init__(...)

:::

方法重写

当子类中定义了一个与父类中同名的方法时,子类的方法会**覆盖(override)**父类的方法。这意味着当你通过子类的实例调用该方法时,会执行子类中定义的方法。

在上面的示例中,StudentTeacher 类都重写了 Person 类的 introduce 方法。重写时,它们还通过 super().introduce() 调用了父类的 introduce 方法,以在添加自己特有信息的同时,保留父类的通用介绍。

多重继承

Python 允许一个子类继承多个父类,这被称为多重继承

语法如下:

python 复制代码
class ChildClass(Parent1, Parent2, ...):

当一个子类继承了多个父类,并且这些父类中有同名的方法或属性时,Python 会遵循**方法解析顺序(Method Resolution Order, MRO)**来查找方法或属性。你可以通过 ChildClass.__mro__ 属性或 help(ChildClass) 来查看 MRO。

:::warning

在 Python 中,MRO(Method Resolution Order,方法解析顺序 ) 是一个决定多重继承下方法或属性查找顺序的规则。它采用 C3 线性化算法 计算,确保类继承关系的一致性和可预测性。

:::

python 复制代码
class Flying:
    def fly(self):
        print("I can fly!")

class Swimming:
    def swim(self):
        print("I can swim!")

# 鸭子既能飞又能游
class Duck(Flying, Swimming):
    def quack(self):
        print("Quack! Quack!")

duck = Duck()
duck.fly()
duck.swim()
duck.quack()

print(Duck.__mro__) # 查看方法解析顺序

:::warning

多重继承虽然强大,但也可能导致复杂的继承关系和"菱形继承问题"(diamond problem)。在使用时需要谨慎,并理解 MRO。

:::