主题
🏗 面向对象编程
函数让代码有条理,但程序复杂到一定程度,纯函数就不够了——你需要"面向对象"这把瑞士军刀。
一切皆对象
在 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)) # Truesuper() — 调用父类方法
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 定义。
练习题
- 设计一个
BankAccount类,包含存款/取款/查询余额,取款时余额不足抛异常 - 写一个
Shape基类和Circle、Rectangle子类,实现area()方法展示多态 - 用
@property实现一个Temperature类,支持摄氏度和华氏度互转 - 给上面的学生管理系统增加按姓名查找学生、导出全班成绩单的功能
- 实现
__eq__让两个学生如果 ID 相同就认为相等
🏗 面向对象让你用更接近现实的方式建模。接下来学 文件操作,把数据存到硬盘上。
🎯 本章要点
- self 代表实例本身。
- 类属性定义在类体内、方法外,所有实例共享一份。
- staticmethod/classmethod/实例方法各有用途,灵活选用。
加载练习题中...