面向对象不是银弹——Python 里你真正需要知道的 OOP

有些教程一上来就是"类是对现实世界的抽象"、“封装继承多态”。

说实话,新手听完更懵了。

这篇换个讲法:用你已有的知识,解释为什么要用类、怎么用、什么时候用。

你已经在用"对象"了

name = "张三"       # name 是 str 类的实例
nums = [1, 2, 3]    # nums 是 list 类的实例
d = {"a": 1}        # d 是 dict 类的实例

print(type(name))   # <class 'str'>
print(type(nums))   # <class 'list'>

对象 = 数据 + 方法。字符串有数据(那些字符),有方法(.upper().split())。列表同理。

类就像模板,对象就是用模板造出来的具体东西。

什么时候需要自己写类?

不需要类的情况:

# 一个简单的数据处理,函数就够
def calculate_average(scores):
    return sum(scores) / len(scores)

需要类的情况:

# 你要处理的东西有状态(数据)+ 行为(方法)
# 比如一个银行账户
account1 = {"name": "张三", "balance": 1000}
account2 = {"name": "李四", "balance": 500}

# 用字典存,写个函数操作
def deposit(account, amount):
    account["balance"] += amount

def withdraw(account, amount):
    if amount > account["balance"]:
        return False
    account["balance"] -= amount
    return True

# 问题是:数据和操作数据的函数是分离的
# 而且任何人可以直接改 account["balance"],不安全

用类改写:

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("存款金额必须大于0")
        self.balance += amount
        return self.balance

    def withdraw(self, amount):
        if amount <= 0:
            raise ValueError("取款金额必须大于0")
        if amount > self.balance:
            raise ValueError("余额不足")
        self.balance -= amount
        return self.balance

    def __str__(self):
        return f"{self.owner}的账户,余额:{self.balance}元"


# 使用
acc = BankAccount("张三", 1000)
acc.deposit(500)
acc.withdraw(200)
print(acc)  # 张三的账户,余额:1300元

数据和操作绑在一起了,外部不能随便改 balance

关键概念拆解

__init__self
class Dog:
    def __init__(self, name, age):
        self.name = name   # self.name 是实例属性,name 是参数
        self.age = age

    def bark(self):
        print(f"{self.name}:汪汪!")

dog1 = Dog("旺财", 3)  # 自动调用 __init__
dog2 = Dog("来福", 2)
print(dog1.name)       # 旺财
dog2.bark()            # 来福:汪汪!

__init__ 是初始化方法,创建对象时自动调用。self 代表实例本身,用来访问属于这个实例的属性和方法。

属性和方法
class Student:
    school = "北京大学"  # 类属性,所有实例共享

    def __init__(self, name, score):
        self.name = name          # 实例属性,每个实例独有
        self.score = score

    def get_grade(self):          # 实例方法
        if self.score >= 90:
            return "A"
        elif self.score >= 60:
            return "C"
        return "F"

s1 = Student("张三", 95)
s2 = Student("李四", 55)
print(s1.school)    # 北京大学(共享的)
print(s1.get_grade())  # A
print(s2.get_grade())  # F

类属性 vs 实例属性:

  • 类属性:所有实例共享,如 school
  • 实例属性:每个实例独有,如 namescore
继承:复用代码
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        pass  # 子类自己实现


class Dog(Animal):
    def speak(self):
        return f"{self.name}:汪汪"


class Cat(Animal):
    def speak(self):
        return f"{self.name}:喵喵"


animals = [Dog("旺财"), Cat("咪咪"), Dog("大黄")]
for a in animals:
    print(a.speak())

父类定义接口,子类各自实现。这就是"多态"——同一个方法名,不同行为。

实战:一个简单的待办事项管理

from datetime import datetime


class TodoItem:
    """一个待办事项"""
    def __init__(self, title, priority="中"):
        self.title = title
        self.priority = priority
        self.completed = False
        self.created_at = datetime.now()

    def complete(self):
        self.completed = True

    def __str__(self):
        status = "[x]" if self.completed else "[ ]"
        return f"{status} [{self.priority}] {self.title}"


class TodoList:
    """待办列表"""
    def __init__(self):
        self.items = []

    def add(self, title, priority="中"):
        item = TodoItem(title, priority)
        self.items.append(item)

    def remove(self, index):
        if 0 <= index < len(self.items):
            return self.items.pop(index)

    def complete(self, index):
        if 0 <= index < len(self.items):
            self.items[index].complete()

    def list_all(self):
        if not self.items:
            print("没有待办事项")
            return
        for i, item in enumerate(self.items):
            print(f"{i}. {item}")


# 使用
todos = TodoList()
todos.add("学习Python", "高")
todos.add("跑步30分钟")
todos.add("写周报", "低")
todos.list_all()
todos.complete(1)
print("\n---完成一项后---")
todos.list_all()

什么时候别用类?

# ❌ 没必要用类(只有一个方法)
class Calculator:
    def add(self, a, b):
        return a + b

# ✅ 函数就够了
def add(a, b):
    return a + b

经验法则:如果你的类只有 __init__ 和一个方法,大概率不需要类。

另一个新手常见过度设计:继承层次太深。能用组合就别用继承。

# ❌ 为了复用而继承
class User(DatabaseConnection):  # 用户不是数据库连接!
    pass

# ✅ 用组合
class User:
    def __init__(self, db):
        self.db = db  # 持有数据库连接,而不是继承它

写在最后

面向对象是一种组织代码的方式,不是写 Python 的必选项。

我自己的经验:写脚本用函数,写项目用类,写框架才用继承。大部分时候 class + def 就够了,别一上来就设计类继承树。

下一篇聊聊 Python 标准库里的好东西——不用装第三方包就能做很多事。

更多推荐