12.面向对象不是银弹——Python 里你真正需要知道的 OOP
·
面向对象不是银弹——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 - 实例属性:每个实例独有,如
name、score
继承:复用代码
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 标准库里的好东西——不用装第三方包就能做很多事。
更多推荐
所有评论(0)