[Python 基础课程]继承
在 Python 的面向对象编程(OOP)中,继承(Inheritance) 是一种重要的机制,它允许一个类(称为子类或派生类)从另一个类(称为父类、基类或超类)中继承属性和方法。
为什么需要继承
考虑我们之前定义的 Person
类,现在我们想创建 Student
和 Teacher
类。它们都属于“人”,所以都有姓名和年龄。
# 传统的做法,没有继承
class Person:def __init__(self, name, age):self.name = nameself.age = agedef 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_iddef 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 = subjectdef introduce(self):# 代码重复print(f"大家好,我叫 {self.name},今年 {self.age} 岁。")print(f"我教 {self.subject}。")
你会发现 name
、age
属性以及 introduce
方法在 Person
、Student
和 Teacher
类中重复出现了。这种代码重复会导致:
- 维护困难: 如果你需要修改“人”的共同行为(比如
introduce
方法),你必须在所有相关的类中都修改一遍。 - 扩展性差: 每增加一个新的“人”的角色(比如
Employee
),你都要重新编写这些通用部分。
继承就是为了解决这些问题: 它允许你定义一个通用的父类,把共同的属性和方法放在父类中。然后,特定的子类可以继承这些共同部分,并添加自己独有的属性和方法,或者修改(重写)父类的行为。
继承的基本用法
在 Python 中,子类在定义时,在类名后面的括号中指定它所继承的父类。
class ChildClass(ParentClass, OtherParentClass):
示例:Person
、Student
和 Teacher
的继承关系
class Person:species = "人类" # 类属性def __init__(self, name, age):self.name = nameself.age = ageprint(f"创建了一个 Person 对象: {self.name}")def introduce(self):print(f"大家好,我叫 {self.name},今年 {self.age} 岁,是 {self.species}。")def celebrate_birthday(self):self.age += 1print(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_idprint(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 中,子类默认会继承父类的所有属性和方法。如果你想让父类的某个属性不被继承,可以通过使用 __ 前缀创建私有属性。
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:# 尝试访问父类的私有属性,会引发 AttributeErrorprint(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)**父类的方法。这意味着当你通过子类的实例调用该方法时,会执行子类中定义的方法。
在上面的示例中,Student
和 Teacher
类都重写了 Person
类的 introduce
方法。重写时,它们还通过 super().introduce()
调用了父类的 introduce
方法,以在添加自己特有信息的同时,保留父类的通用介绍。
多重继承
Python 允许一个子类继承多个父类,这被称为多重继承。
语法如下:
class ChildClass(Parent1, Parent2, ...):
当一个子类继承了多个父类,并且这些父类中有同名的方法或属性时,Python 会遵循**方法解析顺序(Method Resolution Order, MRO)**来查找方法或属性。你可以通过 ChildClass.__mro__
属性或 help(ChildClass)
来查看 MRO。
:::warning
在 Python 中,MRO(Method Resolution Order,方法解析顺序) 是一个决定多重继承下方法或属性查找顺序的规则。它采用 C3 线性化算法 计算,确保类继承关系的一致性和可预测性。
:::
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。
:::