跳至主要内容

Python 继承:可复用代码的最佳实践

Python 继承允许您在复用与扩展现有功能的基础上构建新类。学习如何设计父子类关系、实现继承模式,并应用诸如方法重写等技巧。
· 9分钟 阅读

使用 AI 探索

想象您正在构建一个包含多种用户角色的软件系统,如学生、教师和管理员。这些角色共享姓名和 ID 等通用属性,但也各自需要独有的功能。与其重复编写代码,不如使用继承:您可以在父类中定义共用行为,并在更专业化的子类中进行扩展。

在本文中,我们将探讨 Python 继承,涵盖基础与进阶概念,如方法重写以及 super() 函数。super() 是一个内置函数,会返回一个指向超类的临时对象,从而无需显式写出父类名称也能访问其方法。如果您现在还不太明白也没关系,下面我们会详细讲解。

Python 继承基础

继承是面向对象编程(OOP)的基石之一,它允许一个类(称为子类)从另一个类(称为父类)派生属性和方法。该特性对代码复用至关重要,并简化维护,从而更易于构建可扩展且高效的程序。

定义父类与子类

在继续之前,先看看父类与子类之间的关系。

父类

先从父类开始。父类是子类派生的基础类,它封装了共享的属性与方法。

在 Python 中,父类可这样定义:

class ParentClass:
    def __init__(self, attributes):
        # Initialize attributes
        pass

    def method(self):
        # Define behavior
        pass

子类 

一个子类会继承父类的属性和方法,从而可以直接使用父类中定义的功能。下面的代码展示了子类如何从父类继承属性和方法:

class ChildClass(ParentClass):
    def additional_method(self):
        # Define new behavior
        pass

这种简洁的语法使子类能够利用并扩展父类中定义的功能。

创建一个父类和一个子类

我们用一个更实际的例子:以 Person 作为父类,Student 作为子类。

创建父类

Person 类包含共享属性与一个用于展示信息的方法:    

# Defining the Parent Class
class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id
    def display_info(self):
        return f"Name: {self.name}, ID: {self.id}"

创建子类

Student 类继承自 Person,并新增了 study 方法。

# Defining the Child Class
class Student(Person):
    def study(self):
        return f"{self.name} is studying."

我们来测试一下父类和子类。

# Creating and Testing Instances
student = Student("Samuel", 102)
print(student.display_info())  
print(student.study())      
Name: Samuel, ID: 102
Samuel is studying.

这里发生了什么:

  1. Student 类使用了 Person 的 __init__ 方法来初始化 name 和 id。

  2. study 方法是 Student 独有的,扩展了其功能。

  3. display_info 方法直接从 Person 继承而来。

Python 中的继承类型

正如前面所说,Python 的继承允许类从其他类继承属性与行为,从而实现代码复用与清晰的设计。本节我们来看看不同类型的继承,包括单继承、多继承、多级继承、层次继承与混合继承。

单继承

单继承是指子类仅从一个父类继承,从而在父类功能基础上进行扩展。它适用于对象类型与更广泛类别共享通用属性,同时又需要额外属性或行为的场景。

我们之前的示例就是单继承,接下来更仔细地看一下:在学校管理系统中,所有人员(包括学生、教师和职员)都共享一些通用信息,如 name 和 ID。但学生还会有学术记录,如成绩与所修课程。通过单继承,我们可以创建一个 Person 类来存放共享属性,再用 Student 类扩展学术细节。

single inheritance

单继承。图片作者自制

下面是上述场景的一个示例:

class Person:
    """Represents a general person with basic details."""
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}"

class Student(Person):
    """Represents a student, extending the Person class to include academic details."""
    def __init__(self, name, id, grade, courses):
        super().__init__(name, id)
        self.grade = grade
        self.courses = courses

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}, Courses: {', '.join(self.courses)}"

# Example usage in a school system
student = Student("Samuel", 5678, "B+", ["Math", "Physics", "Computer Science"])
print(student.get_details())
Name: Samuel, ID: 5678, Grade: B+, Courses: Math, Physics, Computer Science

Student 类继承了 Person 的 get_details() 方法,并将其扩展为包含 grade 与 courses。这很好地展示了单继承如何促进模块化代码。

多继承

多继承有点像家谱,允许子类同时从多个父类继承,组合各自的属性与行为。这可能引发冲突,Python 通过方法解析顺序(MRO)来解决。

multiple inheritance

多继承。图片作者自制

来看一个示例:

class Person:
    def get_details(self):
        return "Details of a person."

class Athlete:
    def get_skill(self):
        return "Athletic skills."

class Student(Person, Athlete):
    pass

# Example usage
student = Student()
print(student.get_details())
print(student.get_skill())
Details of a person.
Athletic skills.

可以看到,Student 同时继承了 Person 与 Athlete 的属性和方法。无需额外工作,Student 就能访问来自 Person 的 get_details() 方法以及来自 Athlete 的 get_skill() 方法,有效地将多方功能组合在一起。

不过,多重继承可能引发冲突:如果两个父类都定义了同名的方法或属性怎么办?前面提到的方法解析顺序(MRO)在此就很重要了。它决定查找方法和属性时的搜索顺序,遵循深度优先、从左到右的规则。

您可以使用 __mro__ 属性或 mro() 方法查看某个类的 MRO:

print(Student.__mro__)
(<class '__main__.Student'>, <class '__main__.Person'>, <class '__main__.Athlete'>, <class 'object'>)

多级、层次与混合继承

Python 也支持更复杂的继承结构。我们继续用同一组示例来展示这些概念。

多级继承

多级继承指一个子类继承自另一个子类,而该子类又继承自父类,从而形成一条继承链。

multilevel inheritance

多级继承。图片作者自制

示例:

class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}"

class Student(Person):
    def __init__(self, name, id, grade):
        super().__init__(name, id)
        self.grade = grade

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}"

class GraduateStudent(Student):
    def __init__(self, name, id, grade, thesis_title):
        super().__init__(name, id, grade)
        self.thesis_title = thesis_title

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}, Thesis: {self.thesis_title}"

# Example usage
grad_student = GraduateStudent("Charlie", 91011, "A", "AI in Healthcare")
print(grad_student.get_details())
Name: Charlie, ID: 91011, Grade: A, Thesis: AI in Healthcare

在这里,链条上的每个类都新增了内容:Person 管理姓名和 ID,Student 加入了成绩,GraduateStudent 引入论文题目。借助 super().__init__(),我们复用了初始化逻辑而无需重复代码。既高效又整洁,确保“继承阶梯”的每一层都能正常协同。

层次继承

层次继承指多个子类从同一个父类继承,从而在子类之间共享行为,同时保留各自的独有属性。

hierarchical inheritance

层次继承。图片作者自制

来看一个示例:

class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}"

class Student(Person):
    def __init__(self, name, id, grade):
        super().__init__(name, id)
        self.grade = grade

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}"

class Teacher(Person):
    def __init__(self, name, id, subject):
        super().__init__(name, id)
        self.subject = subject

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Subject: {self.subject}"

# Example usage
student = Student("Samuel", 5678, "B+")
teacher = Teacher("Dr. Smith", 1234, "Math")
print(student.get_details())  
print(teacher.get_details())
Name: Samuel, ID: 5678, Grade: B+
Dr. Smith, ID: 1234, Subject: Math

这里,Person 作为基础,提供通用属性与方法(name、id 与 get_details)。Student 与 Teacher 则在此基础上增加了各自独有的属性(grade 与 subject),并定制了各自的 get_details 方法以反映特定语境。

这种做法让共享功能集中在一个地方(Person 类),而特化行为则整齐地封装在子类中。

混合继承

混合继承结合了多种继承类型(如多级与多继承),用于刻画更复杂的关系。

hybrid inheritance

混合继承。图片作者自制

下面是一个能体现混合继承复杂性的示例。

# Base class
class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}"

# Intermediate class inheriting from the base class
class Employee(Person):
    def __init__(self, name, id, position):
        super().__init__(name, id)
        self.position = position

    def get_position(self):
        return f"Position: {self.position}"

# Another independent base class
class Athlete:
    def __init__(self, sport):
        self.sport = sport

    def get_sport(self):
        return f"Sport: {self.sport}"

# Derived class combining Employee and Athlete
class Student(Employee, Athlete):
    def __init__(self, name, id, position, grade, sport):
        Employee.__init__(self, name, id, position)
        Athlete.__init__(self, sport)
        self.grade = grade

    def get_grade(self):
        return f"Grade: {self.grade}"

# Example usage
student = Student("Samuel", 1234, "Intern", "A", "Soccer")
print(student.get_details())  # From Person
print(student.get_position())  # From Employee
print(student.get_grade())  # From Student
print(student.get_sport())  # From Athlete
Name: Samuel, ID: 1234
Position: Intern
Grade: A
Sport: Soccer

在这个例子中,Student 通过同时继承 Employee(其本身继承自 Person)与 Athlete 的属性与方法,体现了混合继承。这同时结合了层次继承(Employee 继承自 Person)与多继承(Student 同时继承自 Employee 与 Athlete)。

Python 继承的优势

现在,我们来看看优点与可能的不足: 

继承的好处

  1. 可复用性:通过继承,您可以在父类中编写一次代码,并在子类中复用。例如,FullTimeEmployee 与 Contractor 都可以从 Employee 父类继承 get_details() 方法。

  2. 简洁性:继承可以清晰地建模关系。一个典型例子是 FullTimeEmployee 类,它“是”一种 Employee。

  3. 可扩展性:还能在不影响现有代码的情况下添加新特性或新子类。例如,我们可以轻松新增一个 Intern 子类。

继承的潜在局限

  1. 复杂性:这并不意外,过多的继承层级会让代码难以理解。例如,如果 Employee 拥有太多子类(如 Manager、Engineer、Intern 等),就可能变得混乱。

  2. 依赖性:对父类的修改可能会无意中影响所有子类。例如,若您修改了 Employee,可能会导致 FullTimeEmployee 或 Contractor 出错。

  3. 误用:在不合适的场景使用继承会使设计复杂化。比如,不应仅为复用 move() 就让 Car 继承自 Boat,这种关系并不成立。

Python 继承的高级技巧

掌握了继承基础后,我们来看看一些高级技巧。这些技巧(如方法重写、super()、抽象基类与多态)可以增强代码的灵活性,并支持更复杂的设计模式。 

在 Python 中重写方法

方法重写允许子类为父类中已定义的方法提供特定实现。当继承来的行为不能完全满足子类需求时,这非常有用。

class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id
    
    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}"

class Student(Person):
    def __init__(self, name, id, grade):
        super().__init__(name, id)
        self.grade = grade
    
    # Overriding the get_details method
    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}"

# Example usage
student = Student("Samuel", 1234, "A")
print(student.get_details())
Name: Samuel, ID: 1234, Grade: A

这里,Student 重写了 Person 的 get_details() 方法,给出了自己的实现。这样,子类在遵循相同方法名的同时,具备了自身的行为。

为什么要重写?是为了定制继承的行为,并将父类方法的功能裁剪到子类的特定需求。

使用 super() 进行父类初始化

super() 用于在子类中调用父类的方法。当您想扩展或修改父类方法(如构造方法 __Init__())的功能时,它尤其有用。

为什么要用 super()?因为我们希望调用并初始化父类的构造函数,同时避免显式写出父类名。这在多继承场景中特别有帮助。

class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id

class Student(Person):
    def __init__(self, name, id, grade):
        # Using super() to initialize the parent class
        super().__init__(name, id)
        self.grade = grade

# Example usage
student = Student("Samuel", 5678, "B+")
print(student.name)
print(student.id)
print(student.grade)
Samuel
5678
B+

这里,Student 使用 super().__init__(name, id) 调用了 Person 的 __init__,无需重复初始化 name 与 id 的代码。随后,子类再引入自身特有的 grade 属性。

抽象基类(ABC)

抽象基类(ABC)不能直接实例化,它用于定义一组应由其他类实现的通用方法。当您希望确保子类中始终存在某些方法时,ABCs 会很有用。

from abc import ABC, abstractmethod

class Person(ABC):
    @abstractmethod
    def get_details(self):
        pass

class Student(Person):
    def __init__(self, name, id, grade):
        self.name = name
        self.id = id
        self.grade = grade
    
    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}"

# Example usage
student = Student("Hamilton", 7890, "A-")
print(student.get_details())
Name: Hamilton, ID: 7890, Grade: A-

此处的 Person 是抽象类,要求任何子类都必须实现 get_details() 方法。该方法随后由子类 Student 实现。

多态

多态意为“多种形态”。在 Python 中,它允许不同的类使用相同的方法名,但各自以不同的方式实现该方法。

多态帮助我们编写能与不同类对象协同工作的代码,即便这些类的行为不同:

class Person:
    def get_details(self):
        return "Details of a person."

class Student(Person):
    def get_details(self):
        return "Details of a student."

class Teacher(Person):
    def get_details(self):
        return "Details of a teacher."

# Example usage
def print_details(person):
    print(person.get_details())

student = Student()
teacher = Teacher()

print_details(student) 
print_details(teacher) 
Details of a student.
Details of a teacher.

在这个示例中,函数 print_details() 可以接受任何 Person 类型的对象,但会根据对象是 Student 还是 Teacher 调用相应的 get_details() 方法。

常见错误与最佳实践

尽管继承功能强大,但也很容易被误用。这里分享一些建议,帮助您用好这些理念。

避免被重写方法“埋雷”

当子类重写父类方法时,行为可能发生变化。

例如,若父类 Employee 有一个 calculate_pay() 方法,而子类 Manager 在重写时没有覆盖所有情形,就可能导致错误的薪酬计算。

此类情形的最佳实践是:彻底测试被重写的方法,并清晰记录其行为。

在继承与组合之间做选择

虽然本文讨论的是继承,但它并非总是最佳方案。有时通过组合(将对象组合进类而非对其进行扩展)更契合您的需求。

最精炼的区分可概括为:

  • 继承表达“是一个(is-a)”关系。例如,Manager 是一种 Employee。

  • 组合表达“有一个(has-a)”关系。例如,Car 有一个 Engine。

那么何时优先选择组合?当关系并非严格的层级关系,和/或当您希望降低类之间的紧耦合时,请使用组合。

或者也可以说:继承侧重描述关系,而组合更关注功能性。为便于判断,请考虑:

  • 当对象天然呈现层级结构时使用继承。例如,Animal > Bird > Parrot。

  • 当对象共享功能但互不相关时使用组合。例如,Printer 与 Scanner 都使用 DeviceManager。

避免过深的继承链

过深的继承链(父子关系层级过多)会使代码难以阅读和维护。这是个问题,因为对父类的修改可能无意中影响很多子类,且调试也会变得复杂,因为行为分散在多个层级。

此处的最佳实践是:保持层级扁平;并且考虑使用组合(如前所述),或将过深的链拆分为独立的层级结构。

结语

继承是面向对象编程的重要支柱,使开发者能够编写可复用、模块化且可扩展的代码。掌握继承后,您会发现简化复杂系统变得更容易。

一个加深理解的好方法是在项目中尝试构建继承结构。从简单入手,再尝试更复杂的层级,看看它们在实践中的效果。

如果您希望更深入地学习,可以查看我们的 Programming Paradigm Concepts 课程,以更深入地理解继承与其他概念。我们的 Python Developer 职业路径同样是不错的资源,提供系统化的进阶编程技能训练,助您胜任软件开发。

Samuel Shaibu

作者

Samuel Shaibu

资深数据从业者与写作者,热衷于帮助有志于数据领域的专业人士成长与精进。

Python 继承常见问答

什么是 Python 中的继承?

Python 中的继承是一种机制,使一个类可以从另一个类继承属性和方法,从而实现代码复用并构建层次化的类结构。

Python 的继承如何工作?

Python 的继承通过定义一个新类来实现,该类从现有类继承属性和方法,从而形成层次化的类结构。

Python 的继承有哪些类型?

Python 支持多种继承类型,包括单继承、多继承、多级继承和层次继承。

Python 中单继承与多继承有何区别?

单继承只涉及一个父类,而多继承允许一个类同时从多个父类继承。

如何在 Python 中实现继承?

通过在新类定义中指定父类来实现继承,语法为:class ChildClass(ParentClass):。

在 Python 中使用继承有什么好处?

继承促进代码复用,有助于创建层次化类结构,并支持多态,使代码更灵活、更易维护。

如何在 Python 中实现多继承?

通过定义一个同时继承多个基类的类来实现多继承,Python 使用方法解析顺序(MRO)来处理方法查找。

主题