Python 中的面向对象编程(OOP)

更新于 2026-01-11

David Amos

面向对象编程(OOP)在 Python 中帮助你通过将相关数据和行为分组到对象中来组织代码。你首先定义(class),它充当对象的蓝图,然后从中创建对象。OOP 简化了在程序中对现实世界概念的建模,并使你能够构建更具可重用性和可扩展性的系统。

学完本教程后,你将理解以下内容:

  • Python 中的面向对象编程涉及创建类作为对象的蓝图。这些对象包含数据以及操作该数据所需的方法。
  • OOP 的四大核心概念是:封装(Encapsulation)、继承(Inheritance)、抽象(Abstraction)和多态(Polymorphism)。
  • 在 Python 中,通过实例化(instantiating)一个类来创建对象,即调用类名后跟括号。
  • 类继承允许一个类从另一个类(称为父类)继承属性和方法。
  • 使用 super() 可以调用父类中的方法,从而扩展或修改继承的行为。

你将学习如何定义类、实例化类以创建对象,并利用继承构建健壮的 Python 系统。


什么是 Python 中的面向对象编程?

面向对象编程是一种编程范式,它提供了一种组织程序的方式,将属性和行为打包到单个对象中。

例如,一个对象可以代表一个人,具有姓名、年龄和地址等属性,以及走路、说话、呼吸和跑步等行为。或者它可以代表一封电子邮件,具有收件人列表、主题和正文等属性,以及添加附件和发送等行为。

换句话说,面向对象编程是一种对具体、现实世界事物(如汽车)以及事物之间关系(如公司与员工、学生与老师)进行建模的方法。OOP 将现实世界的实体建模为软件对象,这些对象关联某些数据,并能执行特定操作。

OOP 也存在于其他编程语言中,通常被描述为围绕 OOP 的“四大支柱”或“四大原则”:

  • 封装(Encapsulation):允许你将数据(属性)和行为(方法)捆绑在一个类中,形成一个内聚单元。通过定义方法来控制对属性的访问和修改,封装有助于维护数据完整性,并促进模块化和安全的代码。

  • 继承(Inheritance):支持在类之间建立层次关系,使子类能够从父类继承属性和方法。这促进了代码复用并减少了重复。

  • 抽象(Abstraction):专注于隐藏实现细节,只暴露对象的基本功能。通过强制一致的接口,抽象简化了与对象的交互,使开发者关注对象“做什么”而非“如何做”。

  • 多态(Polymorphism):允许你将不同类型的对象视为同一基类型的实例,只要它们实现了通用的接口或行为。Python 的“鸭子类型”(duck typing)使其特别适合多态——你可以直接访问对象的属性和方法,而无需关心其实际类。

本教程将采取实践方式帮助你理解 Python 中的 OOP。但牢记这四个 OOP 核心概念,有助于你更好地记忆所学内容。

关键要点:在 Python 的面向对象编程中,对象处于核心地位。在其他编程范式中,对象仅表示数据;而在 OOP 中,对象还决定了程序的整体结构。


如何在 Python 中定义类?

在 Python 中,使用 class 关键字后跟类名和冒号来定义一个类。然后使用 .__init__() 方法声明每个类实例应具备的属性:

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

但这到底意味着什么?为什么需要类?让我们退一步,考虑使用内置的原始数据结构作为替代方案。

原始数据结构(如数字、字符串和列表)用于表示简单的信息,比如一个苹果的价格、一首诗的名字或你喜欢的颜色。但如果你想表示更复杂的事物呢?

例如,你可能想跟踪一个组织中的员工。你需要存储每位员工的一些基本信息,如姓名、年龄、职位和入职年份。

一种方法是将每位员工表示为一个列表:

kirk = ["James Kirk", 34, "Captain", 2265]
spock = ["Spock", 35, "Science Officer", 2254]
mccoy = ["Leonard McCoy", "Chief Medical Officer", 2266]  # 缺少年龄!

这种方法存在多个问题:

  1. 可读性差:如果你在远离声明处的地方引用 kirk[0],你能记得索引 0 表示员工姓名吗?
  2. 易出错:如果员工列表元素数量不一致(如 mccoy 缺少年龄),会导致逻辑错误。

使用是让这类代码更易管理、更易维护的绝佳方式。


类 vs 实例

类允许你创建用户自定义的数据结构。类定义了称为方法(methods)的函数,用于标识由该类创建的对象可以对其数据执行的行为和操作。

在本教程中,你将创建一个 Dog 类,用于存储关于狗的特征和行为的信息。

  • 是定义某物的蓝图。它本身不包含任何数据。Dog 类规定了定义一只狗需要姓名和年龄,但不包含任何具体狗的姓名或年龄。
  • 实例是从类创建的对象,包含真实数据。Dog 类的一个实例不再是蓝图,而是一只真实的狗,比如 4 岁的 Miles。

换句话说:

  • 就像一张表格或问卷。
  • 实例就像填好信息的表格。许多人可以填写同一张表格,但内容各不相同。

类定义

所有类定义都以 class 关键字开头,后跟类名和冒号。缩进在类定义下的代码被视为类体的一部分。

例如,一个简单的 Dog 类:

# dog.py
class Dog:
    pass

pass 是占位符,表示此处暂无代码,但允许程序运行而不报错。

注意:按惯例,Python 类名采用 CapWords(帕斯卡命名法)。例如,杰克罗素㹴犬的类名为 JackRussellTerrier

为了让 Dog 类更有用,我们为其添加所有狗都应具备的属性(如 nameage)。

你可以在 .__init__() 方法中定义这些属性。每次创建新的 Dog 对象时,.__init__() 会通过赋值初始化对象的状态。

# dog.py
class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
  • self 是第一个参数,Python 自动将其指向新创建的实例。
  • self.name = name 创建一个名为 name实例属性,并赋值。
  • self.age = age 同理。

实例属性的值对每个实例都是唯一的。

相比之下,类属性对所有实例都相同。你可以在 .__init__() 外部定义类属性:

# dog.py
class Dog:
    species = "Canis familiaris"  # 类属性

    def __init__(self, name, age):
        self.name = name   # 实例属性
        self.age = age     # 实例属性
  • 使用类属性表示所有实例共享的值(如物种)。
  • 使用实例属性表示因实例而异的值(如名字、年龄)。

如何在 Python 中实例化一个类?

从类创建新对象称为实例化。语法为:类名()

>>> class Dog:
...     pass
...
>>> Dog()
<__main__.Dog object at 0x106702d30>

输出中的十六进制字符串是对象在内存中的地址。每次实例化都会创建一个新对象:

>>> a = Dog()
>>> b = Dog()
>>> a == b
False  # 即使类型相同,也是不同对象

类属性与实例属性

现在创建一个带类属性 .species 和实例属性 .name.ageDog 类:

>>> class Dog:
...     species = "Canis familiaris"
...     def __init__(self, name, age):
...         self.name = name
...         self.age = age
...

实例化时必须提供 nameage,否则会报错:

>>> Dog()
TypeError: __init__() missing 2 required positional arguments

正确方式:

>>> miles = Dog("Miles", 4)
>>> buddy = Dog("Buddy", 9)

注意:虽然 .__init__() 有三个参数(包括 self),但你只需传两个,因为 Python 自动传入 self

访问属性使用点号(.):

>>> miles.name
'Miles'
>>> miles.age
4
>>> buddy.species
'Canis familiaris'

所有 Dog 实例都保证拥有 .name.age.species 属性。

属性值可动态修改(对象默认是可变的):

>>> buddy.age = 10
>>> miles.species = "Felis silvestris"  # 虽奇怪,但合法!

实例方法

实例方法是在类内部定义的函数,只能在类的实例上调用。和 .__init__() 一样,第一个参数总是 self

# dog.py
class Dog:
    species = "Canis familiaris"

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def description(self):
        return f"{self.name} is {self.age} years old"

    def speak(self, sound):
        return f"{self.name} says {sound}"

使用示例:

>>> miles = Dog("Miles", 4)
>>> miles.description()
'Miles is 4 years old'
>>> miles.speak("Woof Woof")
'Miles says Woof Woof'

但直接打印对象时,输出不友好:

>>> print(miles)
<__main__.Dog object at 0x00aeff70>

可通过定义 .__str__() 方法改善:

# dog.py
class Dog:
    # ...
    def __str__(self):
        return f"{self.name} is {self.age} years old"

现在:

>>> print(miles)
Miles is 4 years old

.__init__().__str__() 这类以双下划线开头和结尾的方法称为 dunder 方法(double underscore)。它们用于自定义类的行为。


如何在 Python 中从另一个类继承?

继承是指一个类获得另一个类的属性和方法的过程。新类称为子类(child class),被继承的类称为父类(parent class)。

继承语法:class 子类(父类):

# inheritance.py
class Parent:
    hair_color = "brown"

class Child(Parent):
    pass

Child 自动拥有 hair_color = "brown"

子类可以覆盖扩展父类的属性和方法:

class Child(Parent):
    hair_color = "purple"  # 覆盖

或扩展:

class Parent:
    speaks = ["English"]

class Child(Parent):
    def __init__(self):
        super().__init__()
        self.speaks.append("German")  # 扩展

示例:狗狗公园

假设你在狗狗公园,有不同品种的狗。你想用 Python 建模。

最初版本的 Dog 类无法区分品种。你可以添加 breed 属性,但更好的方式是使用继承

父类 vs 子类

保留通用 Dog 类:

class Dog:
    species = "Canis familiaris"

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f"{self.name} is {self.age} years old"

    def speak(self, sound):
        return f"{self.name} says {sound}"

为每种狗创建子类:

class JackRussellTerrier(Dog):
    pass

class Dachshund(Dog):
    pass

class Bulldog(Dog):
    pass

创建实例:

>>> miles = JackRussellTerrier("Miles", 4)
>>> buddy = Dachshund("Buddy", 9)
>>> jack = Bulldog("Jack", 3)

子类实例继承父类所有属性和方法:

>>> miles.species
'Canis familiaris'
>>> print(jack)
Jack is 3 years old

使用 type() 查看具体类型:

>>> type(miles)
<class '__main__.JackRussellTerrier'>

使用 isinstance() 检查是否为某类(包括父类)的实例:

>>> isinstance(miles, Dog)
True
>>> isinstance(miles, Bulldog)
False

所有子类实例都是父类的实例,但不一定是其他子类的实例。


扩展父类功能

不同品种的狗叫声不同。我们希望为每种狗设置默认叫声。

在子类中覆盖 speak() 方法:

class JackRussellTerrier(Dog):
    def speak(self, sound="Arf"):
        return f"{self.name} says {sound}"

现在:

>>> miles.speak()
'Miles says Arf'
>>> miles.speak("Grrr")  # 仍可自定义
'Miles says Grrr'

但如果父类的 speak() 方法格式变了(比如改为 "barks"),子类不会自动更新,因为它完全覆盖了父类方法。

使用 super() 调用父类方法

为了既保留默认声音,又继承父类的格式,使用 super()

class JackRussellTerrier(Dog):
    def speak(self, sound="Arf"):
        return super().speak(sound)  # 调用父类的 speak

现在如果父类改为:

def speak(self, sound):
    return f"{self.name} barks: {sound}"

则:

>>> miles.speak()
'Miles barks: Arf'

完美结合了默认值和父类格式!

注意super() 不仅查找直接父类,还会遍历整个类继承链。在复杂继承中需谨慎使用。


总结

在本教程中,你学习了 Python 中的面向对象编程(OOP)。OOP 是 Java、C#、C++ 等现代语言的核心范式,所学知识具有广泛适用性。

你学会了如何:

  • 定义类(对象的蓝图)
  • 实例化类以创建对象
  • 使用属性和方法定义对象的特性和行为
  • 使用继承从父类创建子类
  • 使用 super() 引用父类方法
  • 使用 isinstance() 检查对象是否继承自某类

掌握 OOP 将帮助你编写更清晰、可维护、可扩展的 Python 程序。