第七章 · 面向对象

魔法方法

本节目标

学完这一节,你会知道:

  1. 什么是魔法方法
  2. __str____repr__ 有什么用
  3. 如何让对象支持 len()
  4. 如何让对象支持 + 运算
  5. 如何让自定义类用起来更像 Python 内置类型

魔法方法不是魔法,它只是 Python 约定的一组特殊方法名。

先跑一个例子

新建文件 magic_demo.py,写入:

class Book:
    def __init__(self, title, price):
        self.title = title
        self.price = price

    def __str__(self):
        return f"《{self.title}》售价 {self.price} 元"


book = Book("Python 入门", 59)
print(book)

运行:

python3 magic_demo.py

你会看到:

《Python 入门》售价 59 元

如果没有 __str__(),打印对象时会显示一串不友好的默认信息。

什么是魔法方法?

魔法方法是前后都有双下划线的方法,比如:

__init__
__str__
__len__
__add__

这些方法会在特定语法中被 Python 自动调用。

例如:

  • 创建对象时调用 __init__
  • print(obj) 时调用 __str__
  • len(obj) 时调用 __len__
  • obj1 + obj2 时调用 __add__

str:给用户看的字符串

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def __str__(self):
        return f"{self.name}{self.score} 分"


student = Student("小明", 85)
print(student)

输出:

小明:85 分

__str__() 必须返回字符串。

repr:给开发者看的字符串

__repr__() 通常用于调试,尽量返回能看清对象内容的字符串。

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def __repr__(self):
        return f"Student(name={self.name!r}, score={self.score!r})"


student = Student("小明", 85)
print(repr(student))

!r 会使用更适合调试的表示方式,比如字符串会带引号。

如果只写一个,初学时先写 __str__() 就够了。

len:支持 len()

class Team:
    def __init__(self, name):
        self.name = name
        self.members = []

    def add_member(self, member):
        self.members.append(member)

    def __len__(self):
        return len(self.members)


team = Team("Python 学习小组")
team.add_member("小明")
team.add_member("小红")

print(len(team))

len(team) 会自动调用 team.__len__()

add:支持加法

class Money:
    def __init__(self, amount):
        self.amount = amount

    def __add__(self, other):
        return Money(self.amount + other.amount)

    def __str__(self):
        return f"{self.amount} 元"


a = Money(10)
b = Money(20)

print(a + b)

输出:

30 元

a + b 会自动调用 a.__add__(b)

eq:支持相等判断

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def __eq__(self, other):
        return self.name == other.name and self.price == other.price


p1 = Product("苹果", 3.5)
p2 = Product("苹果", 3.5)

print(p1 == p2)

如果不写 __eq__(),两个不同对象通常不会因为内容相同就相等。

逐行拆解

再看开头的例子:

def __str__(self):
    return f"《{self.title}》售价 {self.price} 元"

定义对象被转换成字符串时的显示方式。

print(book)

Python 发现要打印 book,就自动调用 book.__str__()

自己改一改

magic_demo.py 改成:

class Course:
    def __init__(self, title, lessons):
        self.title = title
        self.lessons = lessons

    def __str__(self):
        return f"{self.title},共 {self.lessons} 节"

    def __len__(self):
        return self.lessons


course = Course("Python 入门", 25)
print(course)
print(len(course))

然后继续改:

  1. 增加 __repr__()
  2. 创建两个课程对象
  3. __eq__() 判断两个课程标题是否相同

了解即可:让对象像列表

如果实现 __getitem__(),对象可以用索引访问。

class Playlist:
    def __init__(self):
        self.songs = []

    def add(self, song):
        self.songs.append(song)

    def __len__(self):
        return len(self.songs)

    def __getitem__(self, index):
        return self.songs[index]


playlist = Playlist()
playlist.add("歌曲 A")
playlist.add("歌曲 B")

print(len(playlist))
print(playlist[0])

这类方法以后写自定义容器时会用到。

常见魔法方法速查

方法 触发方式
__init__ 创建对象
__str__ print(obj)str(obj)
__repr__ repr(obj)、调试显示
__len__ len(obj)
__add__ obj + other
__eq__ obj == other
__getitem__ obj[index]
__contains__ item in obj
__iter__ for item in obj

不用一次背完。看到语法时,知道背后会调用对应方法就行。

常见错误

1. str 返回的不是字符串

错误写法:

def __str__(self):
    return 123

__str__() 必须返回字符串。

2. 魔法方法名字写错

def _str_(self):
    return "hello"

前后都要两个下划线:

def __str__(self):
    return "hello"

3. add 没有返回新对象

如果 a + b 需要产生结果,__add__() 应该返回一个结果,而不是只打印。

4. 过度使用魔法方法

魔法方法应该让对象更自然,而不是让代码更难懂。只有当行为符合直觉时才使用。

小练习

练习 1:Book 显示

定义 Book 类,实现 __str__(),让 print(book) 输出书名和作者。

练习 2:购物车长度

定义 Cart 类,里面有商品列表,实现 __len__() 返回商品数量。

练习 3:金额相加

定义 Money 类,实现 __add__(),让两个金额对象可以相加。

参考答案

练习 1:

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

    def __str__(self):
        return f"《{self.title}》,作者:{self.author}"

练习 2:

class Cart:
    def __init__(self):
        self.items = []

    def add(self, item):
        self.items.append(item)

    def __len__(self):
        return len(self.items)


cart = Cart()
cart.add("苹果")
cart.add("牛奶")
print(len(cart))

练习 3:

class Money:
    def __init__(self, amount):
        self.amount = amount

    def __add__(self, other):
        return Money(self.amount + other.amount)

    def __str__(self):
        return f"{self.amount} 元"


print(Money(10) + Money(20))

小结

这一节你学会了:

  1. 魔法方法是 Python 自动调用的特殊方法
  2. __str__() 控制对象打印效果
  3. __repr__() 更适合调试显示
  4. __len__() 让对象支持 len()
  5. __add__()__eq__() 可以让对象支持运算和比较

到这里,你已经走完了 Python 基础教程的主要路线。接下来可以把这些知识组合起来,做命令行工具、网页后端、爬虫或自动化脚本。

魔法方法不玄,只是 Python 早就约好了暗号

__str__、__len__、__add__ 这些名字看着像秘密咒语,其实是在告诉 Python:我的对象也想支持打印、求长度、相加。先让 Book、Cart、Money 跑起来,你会发现自定义对象也能变得很顺手。

讨论 (0)

还没有评论,来抢沙发吧!