在 Python 的面向对象编程(OOP)中,继承(Inheritance) 是一种重要的机制,它允许一个类(称为子类 或派生类 )从另一个类(称为父类 、基类 或超类 )中继承属性和方法。
为什么需要继承
考虑我们之前定义的 Person
类,现在我们想创建 Student
和 Teacher
类。它们都属于"人",所以都有姓名和年龄。
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}。")
你会发现 name
、age
属性以及 introduce
方法在 Person
、Student
和 Teacher
类中重复出现了。这种代码重复会导致:
- 维护困难: 如果你需要修改"人"的共同行为(比如
introduce
方法),你必须在所有相关的类中都修改一遍。 - 扩展性差: 每增加一个新的"人"的角色(比如
Employee
),你都要重新编写这些通用部分。
继承就是为了解决这些问题: 它允许你定义一个通用的父类 ,把共同的属性和方法放在父类中。然后,特定的子类 可以继承这些共同部分,并添加自己独有的属性和方法,或者修改(重写)父类的行为。
继承的基本用法
在 Python 中,子类在定义时,在类名后面的括号中指定它所继承的父类。
python
class ChildClass(ParentClass, OtherParentClass):
示例:Person
、Student
和 Teacher
的继承关系
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)**父类的方法。这意味着当你通过子类的实例调用该方法时,会执行子类中定义的方法。
在上面的示例中,Student
和 Teacher
类都重写了 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。
:::