day-027-面向对象-下

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,就是个普通函数

今日踩坑记录

  1. __lt__ 和 __gt__ 只需实现一个 :__lt__(小于)就足够 sorted() 用了。Python 会自动推导 __gt__(大于)。
  2. 特殊方法要返回新对象还是修改自身 :__add__ 应该返回新对象(不修改自身),__iadd__(+=)才修改自身。
  3. 类方法用错了场景:如果不需要用到类属性或创建实例,普通函数就够了,别硬塞进类里。

六、明天学什么?

OOP 三连结束。明天开始综合练习------把前27天学的全部东西用起来,做一个完整的命令行项目。


特殊方法让你的类融入 Python 生态,类方法让类本身也能做事情。OOP 的工具箱到此齐了。
第27天,打卡完成。明天见!


本系列是个人学习笔记,如有错误欢迎在评论区指正交流。

相关推荐
bkspiderx1 小时前
Qt 插件机制:动态扩展应用功能的核心框架
开发语言·qt·元数据·qt 插件·qpluginloader
程序员老陆2 小时前
深入理解 C++ thread_local:线程私有存储的正确打开方式
开发语言·c++·程序设计
沐晓时光2 小时前
C语言入门,深入理解指针(3)
c语言·开发语言
码云数智-园园2 小时前
unique_ptr 还是 shared_ptr?C++ 智能指针选型与内存泄漏实战分析
java·开发语言
禾小西3 小时前
07丨Redis 哨兵机制:主库故障后,如何恢复服务?
java·开发语言·redis
我不是阵雨3 小时前
JDK 21虚拟线程Pinning陷阱:一文拔钉解困
java·开发语言
全栈练习生3 小时前
AI Agent 沙箱
python·ai
徐小黑ACG3 小时前
Golang 基础05 结构体struct
开发语言·算法·golang
优橙教育4 小时前
零基础学AI应用开发要多久?3个月能到什么水平
服务器·开发语言·网络·php
SEO_juper4 小时前
用 Python 写一个 GEO 可见性检查脚本:你的网站现在能被 AI 引用吗
开发语言·人工智能·爬虫·python·seo·外贸独立站