Day 27:面向对象(下)------特殊方法与类方法
OOP 最后一篇。学会 Python 的"魔法方法"让你的类像内置类型一样好用,了解类方法和静态方法的区别。最后做一个完整的小项目。
一、特殊方法(魔术方法)------让你的类更像 Python
以 __ 开头结尾的方法称为特殊方法(也叫魔术方法)。定义它们能让你的类支持 len()、[]、+、for...in 等内置操作。
__len__ 和 __getitem__------支持 len() 和 \[\]
python
class Playlist:
"""歌单类"""
def __init__(self, name):
self.name = name
self.songs = []
def add_song(self, song):
self.songs.append(song)
def __len__(self):
"""让 len() 可用"""
return len(self.songs)
def __getitem__(self, index):
"""让 [] 索引可用"""
return self.songs[index]
def __str__(self):
return f"《{self.name}》共 {len(self)} 首歌"
# 使用
pl = Playlist("我的最爱")
pl.add_song("晴天")
pl.add_song("夜曲")
pl.add_song("稻香")
print(len(pl)) # 3 因为实现了 __len__
print(pl[0]) # 晴天 因为实现了 __getitem__
print(pl[1:]) # ['夜曲', '稻香'] 切片也行!
for song in pl: # 实现了 __getitem__,就能 for 循环
print(song)
__eq__、__lt__ 等------支持比较运算
python
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __eq__(self, other):
"""== 操作"""
return self.score == other.score
def __lt__(self, other):
"""< 操作(实现 __lt__ 后 sorted() 也能用)"""
return self.score < other.score
def __repr__(self):
return f"Student({self.name}, {self.score})"
s1 = Student("小明", 85)
s2 = Student("小红", 92)
s3 = Student("小刚", 78)
print(s1 == s2) # False (比较成绩)
print(s1 < s2) # True
students = [s1, s2, s3]
students.sort() # 按成绩排序(因为实现了 __lt__)
print(students) # [Student(小刚, 78), Student(小明, 85), Student(小红, 92)]
__add__、__mul__ 等------支持运算符
python
class Vector:
"""二维向量"""
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
"""支持 + 运算"""
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, scalar):
"""支持 * 标量"""
return Vector(self.x * scalar, self.y * scalar)
def __repr__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # Vector(4, 6)
print(v1 * 3) # Vector(3, 6)
二、类方法 @classmethod------操作类本身
python
class Student:
school = "海淀小学"
def __init__(self, name, score):
self.name = name
self.score = score
# 实例方法:第一个参数是 self(实例)
def info(self):
return f"{self.name} ({self.school})"
# 类方法:第一个参数是 cls(类本身)
@classmethod
def change_school(cls, new_school):
cls.school = new_school
# 类方法的典型用法:替代构造器
@classmethod
def from_string(cls, string):
"""从字符串创建 Student,如 "小明,85" """
name, score = string.split(",")
return cls(name.strip(), int(score))
# 使用类方法改变类属性
Student.change_school("朝阳小学")
print(Student.school) # 朝阳小学
# 使用类方法创建实例(工厂方法)
s = Student.from_string("小红, 92")
print(s.info()) # 小红 (朝阳小学)
三、静态方法 @staticmethod------不属于实例也不属于类
python
class MathUtils:
@staticmethod
def is_even(n):
return n % 2 == 0
@staticmethod
def clamp(value, min_val, max_val):
"""把 value 限制在 [min_val, max_val] 之间"""
return max(min_val, min(value, max_val))
# 静态方法不需要 self 或 cls,直接通过类名调用
print(MathUtils.is_even(10)) # True
print(MathUtils.clamp(15, 0, 10)) # 10
三种方法对比:
| 方法类型 | 装饰器 | 第一个参数 | 调用方式 |
|---|---|---|---|
| 实例方法 | 无 | self |
obj.method() |
| 类方法 | @classmethod |
cls |
Class.method() 或 obj.method() |
| 静态方法 | @staticmethod |
无 | Class.method() |
四、实战------学生管理系统(面向对象版)
python
class Student:
"""学生类"""
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
@property
def grade(self):
if self.score >= 90:
return "A"
elif self.score >= 80:
return "B"
elif self.score >= 60:
return "C"
return "D"
def __str__(self):
return f"{self.name}, {self.age}岁, {self.score}分 ({self.grade})"
def __repr__(self):
return f"Student('{self.name}', {self.age}, {self.score})"
class StudentManager:
"""管理学生集合"""
def __init__(self):
self.students = []
def add(self, name, age, score):
s = Student(name, int(age), int(score))
self.students.append(s)
return s
def remove(self, name):
self.students = [s for s in self.students if s.name != name]
def find(self, name):
for s in self.students:
if s.name == name:
return s
return None
def __len__(self):
return len(self.students)
def __iter__(self):
"""让 for s in manager 可用"""
return iter(self.students)
def top_students(self, n=3):
"""前 n 名"""
return sorted(self.students, key=lambda s: s.score, reverse=True)[:n]
@property
def average(self):
if not self.students:
return 0
return sum(s.score for s in self.students) / len(self)
@classmethod
def from_csv(cls, filepath):
"""从 CSV 文件创建管理器"""
import csv
manager = cls()
with open(filepath, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
manager.add(row["姓名"], row["年龄"], row["成绩"])
return manager
# 使用
mgr = StudentManager()
mgr.add("小明", 18, 85)
mgr.add("小红", 19, 92)
mgr.add("小刚", 18, 78)
mgr.add("小丽", 20, 95)
print(f"共 {len(mgr)} 名学生,平均分 {mgr.average:.1f}")
print("前3名:")
for s in mgr.top_students():
print(f" {s}")
五、今日学习总结
| 学习内容 | 掌握情况 | 一句话要点 |
|---|---|---|
__len__ / __getitem__ |
✅ 重点 | 让对象支持 len() 和 \[\] |
__eq__ / __lt__ |
✅ 理解 | 比较与排序 |
__add__ / __mul__ |
✅ 了解 | 运算符重载 |
| @classmethod | ✅ 重点 | 第一个参数是 cls,操作类本身 |
| @staticmethod | ✅ 理解 | 不需要 self/cls,就是个普通函数 |
今日踩坑记录
__lt__和__gt__只需实现一个 :__lt__(小于)就足够 sorted() 用了。Python 会自动推导__gt__(大于)。- 特殊方法要返回新对象还是修改自身 :
__add__应该返回新对象(不修改自身),__iadd__(+=)才修改自身。 - 类方法用错了场景:如果不需要用到类属性或创建实例,普通函数就够了,别硬塞进类里。
六、明天学什么?
OOP 三连结束。明天开始综合练习------把前27天学的全部东西用起来,做一个完整的命令行项目。
特殊方法让你的类融入 Python 生态,类方法让类本身也能做事情。OOP 的工具箱到此齐了。
第27天,打卡完成。明天见!
本系列是个人学习笔记,如有错误欢迎在评论区指正交流。