Day 27:面向对象(下)——特殊方法与类方法
OOP 最后一篇。学会 Python 的"魔法方法"让你的类像内置类型一样好用,了解类方法和静态方法的区别。最后做一个完整的小项目。
一、特殊方法(魔术方法)——让你的类更像 Python
以__开头结尾的方法称为特殊方法(也叫魔术方法)。定义它们能让你的类支持len()、[]、+、for...in等内置操作。
__len__和__getitem__——支持 len() 和 []
classPlaylist:"""歌单类"""def__init__(self,name):self.name=name self.songs=[]defadd_song(self,song):self.songs.append(song)def__len__(self):"""让 len() 可用"""returnlen(self.songs)def__getitem__(self,index):"""让 [] 索引可用"""returnself.songs[index]def__str__(self):returnf"《{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:])# ['夜曲', '稻香'] 切片也行!forsonginpl:# 实现了 __getitem__,就能 for 循环print(song)__eq__、__lt__等——支持比较运算
classStudent:def__init__(self,name,score):self.name=name self.score=scoredef__eq__(self,other):"""== 操作"""returnself.score==other.scoredef__lt__(self,other):"""< 操作(实现 __lt__ 后 sorted() 也能用)"""returnself.score<other.scoredef__repr__(self):returnf"Student({self.name},{self.score})"s1=Student("小明",85)s2=Student("小红",92)s3=Student("小刚",78)print(s1==s2)# False (比较成绩)print(s1<s2)# Truestudents=[s1,s2,s3]students.sort()# 按成绩排序(因为实现了 __lt__)print(students)# [Student(小刚, 78), Student(小明, 85), Student(小红, 92)]__add__、__mul__等——支持运算符
classVector:"""二维向量"""def__init__(self,x,y):self.x=x self.y=ydef__add__(self,other):"""支持 + 运算"""returnVector(self.x+other.x,self.y+other.y)def__mul__(self,scalar):"""支持 * 标量"""returnVector(self.x*scalar,self.y*scalar)def__repr__(self):returnf"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——操作类本身
classStudent:school="海淀小学"def__init__(self,name,score):self.name=name self.score=score# 实例方法:第一个参数是 self(实例)definfo(self):returnf"{self.name}({self.school})"# 类方法:第一个参数是 cls(类本身)@classmethoddefchange_school(cls,new_school):cls.school=new_school# 类方法的典型用法:替代构造器@classmethoddeffrom_string(cls,string):"""从字符串创建 Student,如 "小明,85" """name,score=string.split(",")returncls(name.strip(),int(score))# 使用类方法改变类属性Student.change_school("朝阳小学")print(Student.school)# 朝阳小学# 使用类方法创建实例(工厂方法)s=Student.from_string("小红, 92")print(s.info())# 小红 (朝阳小学)三、静态方法 @staticmethod——不属于实例也不属于类
classMathUtils:@staticmethoddefis_even(n):returnn%2==0@staticmethoddefclamp(value,min_val,max_val):"""把 value 限制在 [min_val, max_val] 之间"""returnmax(min_val,min(value,max_val))# 静态方法不需要 self 或 cls,直接通过类名调用print(MathUtils.is_even(10))# Trueprint(MathUtils.clamp(15,0,10))# 10三种方法对比:
| 方法类型 | 装饰器 | 第一个参数 | 调用方式 |
|---|---|---|---|
| 实例方法 | 无 | self | obj.method() |
| 类方法 | @classmethod | cls | Class.method()或obj.method() |
| 静态方法 | @staticmethod | 无 | Class.method() |
四、实战——学生管理系统(面向对象版)
classStudent:"""学生类"""def__init__(self,name,age,score):self.name=name self.age=age self.score=score@propertydefgrade(self):ifself.score>=90:return"A"elifself.score>=80:return"B"elifself.score>=60:return"C"return"D"def__str__(self):returnf"{self.name},{self.age}岁,{self.score}分 ({self.grade})"def__repr__(self):returnf"Student('{self.name}',{self.age},{self.score})"classStudentManager:"""管理学生集合"""def__init__(self):self.students=[]defadd(self,name,age,score):s=Student(name,int(age),int(score))self.students.append(s)returnsdefremove(self,name):self.students=[sforsinself.studentsifs.name!=name]deffind(self,name):forsinself.students:ifs.name==name:returnsreturnNonedef__len__(self):returnlen(self.students)def__iter__(self):"""让 for s in manager 可用"""returniter(self.students)deftop_students(self,n=3):"""前 n 名"""returnsorted(self.students,key=lambdas:s.score,reverse=True)[:n]@propertydefaverage(self):ifnotself.students:return0returnsum(s.scoreforsinself.students)/len(self)@classmethoddeffrom_csv(cls,filepath):"""从 CSV 文件创建管理器"""importcsv manager=cls()withopen(filepath,"r",encoding="utf-8")asf:reader=csv.DictReader(f)forrowinreader:manager.add(row["姓名"],row["年龄"],row["成绩"])returnmanager# 使用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名:")forsinmgr.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天,打卡完成。明天见!
本系列是个人学习笔记,如有错误欢迎在评论区指正交流。