Skip to content

🏗 面向对象编程 ​

函数让代码有条理,但程序复杂到一定程度,纯函数就不够了——你需要"面向对象"这把瑞士军刀。

一切皆对象 ​

在 Python 里,数字、字符串、函数、模块……一切都是对象。面向对象编程(OOP)就是用"类"定义对象的模板,再用模板创建实例。

类与实例 ​

python
class Dog:
    """狗类"""
    species = "犬科"  # 类属性(所有实例共享)

    def __init__(self, name, age):
        """初始化方法 — 创建实例时自动调用"""
        self.name = name    # 实例属性(每个实例独有)
        self.age = age

    def bark(self):
        """实例方法"""
        print(f"{self.name}:汪汪!")

    def birthday(self):
        self.age += 1
        print(f"{self.name} {self.age} 岁啦!")

# 创建实例
buddy = Dog("Buddy", 3)
buddy.bark()       # Buddy:汪汪!
buddy.birthday()   # Buddy 4 岁啦!

self 是什么?

self 代表实例本身。调用 buddy.bark() 时,Python 自动把 buddy 传给 self。名字叫 self 是约定俗成,写别的也能跑,但大家都这么写。

三种方法 ​

python
class Calculator:
    factor = 10  # 类属性

    def __init__(self, brand):
        self.brand = brand

    def add(self, a, b):
        """实例方法 — 需要 self,可以访问实例和类的数据"""
        return a + b

    @classmethod
    def multiply_factor(cls, x):
        """类方法 — 需要 cls,可以访问类数据但不能访问实例数据"""
        return x * cls.factor

    @staticmethod
    def help():
        """静态方法 — 不需要 self 或 cls,就是个普通函数放在类里"""
        print("这是一个计算器类")

# 使用
c = Calculator("卡西欧")
print(c.add(3, 5))                 # 8(实例方法)
print(Calculator.multiply_factor(5))  # 50(类方法,可以直接用类名调)
Calculator.help()                  # 静态方法

什么时候用哪种?

  • 实例方法:需要访问实例数据(self.xxx)→ 最常用
  • 类方法:需要访问类数据但不关心具体实例 → 工厂方法、配置类
  • 静态方法:逻辑上和类相关但不依赖类和实例 → 工具函数

继承 ​

子类继承父类的所有属性和方法,可以重写、扩展:

python
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return "..."

class Cat(Animal):
    def speak(self):          # 重写父类方法
        return "喵喵!"

    def purr(self):           # 扩展新方法
        print(f"{self.name} 在咕噜咕噜")

class Dog(Animal):
    def speak(self):
        return "汪汪!"

# 多态 — 不同类的同名方法,行为不同
def animal_sound(animal):
    print(f"{animal.name} 说: {animal.speak()}")

animal_sound(Cat("小花"))   # 小花 说: 喵喵!
animal_sound(Dog("旺财"))   # 旺财 说: 汪汪!

# 判断类型
print(isinstance(Cat("咪咪"), Animal))  # True
print(issubclass(Cat, Animal))          # True

super() — 调用父类方法 ​

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

class Student(Person):
    def __init__(self, name, age, student_id):
        super().__init__(name, age)  # 先调父类的 __init__
        self.student_id = student_id
        self.grades = []

    def add_grade(self, grade):
        self.grades.append(grade)

@property — 优雅的 getter/setter ​

python
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = balance  # 前面加 _ 表示"私有"(约定)

    @property
    def balance(self):
        """读取余额 — 像访问属性一样"""
        return self._balance

    @balance.setter
    def balance(self, amount):
        """设置余额 — 可以加入校验逻辑"""
        if amount < 0:
            raise ValueError("余额不能为负数!")
        self._balance = amount

account = BankAccount("小雅", 1000)
print(account.balance)   # 1000(不用写 account.balance()!)
account.balance = 2000   # 触发 setter 的校验

魔术方法 ​

以双下划线开头和结尾的方法,Python 在特定场景自动调用:

python
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        """print() 或 str() 时调用 — 给人类看的"""
        return f"Vector({self.x}, {self.y})"

    def __repr__(self):
        """repr() 或调试时调用 — 给开发者看的,最好能 eval 还原"""
        return f"Vector({self.x}, {self.y})"

    def __add__(self, other):
        """+ 运算符"""
        return Vector(self.x + other.x, self.y + other.y)

    def __len__(self):
        """len() 函数"""
        return 2

    def __eq__(self, other):
        """== 比较"""
        return self.x == other.x and self.y == other.y

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2)        # Vector(4, 6)
print(v1 == Vector(1, 2))  # True

实战:学生管理系统 ​

python
# /opt/kb/docs/python/oop.py — 配套示例代码

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

    def __str__(self):
        return f"{self.name}({self.age}岁)"

class Student(Person):
    def __init__(self, name, age, student_id):
        super().__init__(name, age)
        self.student_id = student_id
        self._grades = {}

    def add_grade(self, subject, score):
        if not 0 <= score <= 100:
            raise ValueError("分数必须在 0-100 之间")
        self._grades[subject] = score

    @property
    def average(self):
        if not self._grades:
            return 0
        return sum(self._grades.values()) / len(self._grades)

    @property
    def grades(self):
        return self._grades.copy()  # 返回副本,保护原始数据

    def __str__(self):
        return f"[{self.student_id}] {self.name},平均分: {self.average:.1f}"


class Teacher(Person):
    def __init__(self, name, age, subject):
        super().__init__(name, age)
        self.subject = subject
        self.students = []

    def add_student(self, student):
        self.students.append(student)

    def class_average(self):
        if not self.students:
            return 0
        total = sum(s.average for s in self.students)
        return total / len(self.students)

    def __str__(self):
        return f"👨‍🏫 {self.name}老师({self.subject}),{len(self.students)}名学生"


# --- 使用示例 ---
if __name__ == "__main__":
    s1 = Student("小明", 15, "S001")
    s1.add_grade("数学", 92)
    s1.add_grade("语文", 85)
    s1.add_grade("英语", 88)

    s2 = Student("小红", 15, "S002")
    s2.add_grade("数学", 78)
    s2.add_grade("语文", 95)
    s2.add_grade("英语", 82)

    t = Teacher("王", 32, "Python编程")
    t.add_student(s1)
    t.add_student(s2)

    print(t)              # 👨‍🏫 王老师(Python编程),2名学生
    print(s1)             # [S001] 小明,平均分: 88.3
    print(f"班级平均分: {t.class_average():.1f}")  # 班级平均分: 86.7

常见坑 ​

坑1:多继承与 MRO

Python 支持多继承但容易踩坑。当多个父类有同名方法时,Python 用 MRO(Method Resolution Order,C3 线性化算法)决定调用顺序。可以用 ClassName.__mro__ 查看。遇到多继承尽量克制——能用组合就别用多继承。

坑2:类变量 vs 实例变量

python
class Team:
    members = []  # 类变量 — 所有实例共享!

t1 = Team()
t2 = Team()
t1.members.append("张三")
print(t2.members)  # ['张三'] ← 意外!

类变量是所有实例共享的同一个对象。实例独有的数据一定要在 __init__ 里用 self.xxx 定义。

练习题 ​

  1. 设计一个 BankAccount 类,包含存款/取款/查询余额,取款时余额不足抛异常
  2. 写一个 Shape 基类和 Circle、Rectangle 子类,实现 area() 方法展示多态
  3. 用 @property 实现一个 Temperature 类,支持摄氏度和华氏度互转
  4. 给上面的学生管理系统增加按姓名查找学生、导出全班成绩单的功能
  5. 实现 __eq__ 让两个学生如果 ID 相同就认为相等

🏗 面向对象让你用更接近现实的方式建模。接下来学 文件操作,把数据存到硬盘上。


🎯 本章要点 ​

  • self 代表实例本身。
  • 类属性定义在类体内、方法外,所有实例共享一份。
  • staticmethod/classmethod/实例方法各有用途,灵活选用。
加载练习题中...

有问题或补充?欢迎留言