Python 魔术方法详解
魔术方法(Magic Methods),也称为特殊方法(Special Methods)或双下划线方法(Dunder Methods),是 Python 中以双下划线开头和结尾的特殊方法。这些方法定义了对象在特定操作下的行为,是 Python 面向对象编程的核心机制之一。
📚 学习目标
通过本章学习,你将能够:
- 理解魔术方法的概念和作用机制
- 掌握常用魔术方法的使用方法
- 学会自定义对象的特殊行为
- 实现对象的运算符重载
- 控制对象的生命周期和表示形式
🔍 魔术方法概述
什么是魔术方法
魔术方法是 Python 中以双下划线__包裹的特殊方法,它们定义了对象在特定操作下的行为。当我们对对象执行某些操作时,Python 会自动调用相应的魔术方法。
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"Point({self.x}, {self.y})"
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
## 创建对象时自动调用 __init__
p1 = Point(1, 2)
## 打印对象时自动调用 __str__
print(p1) # 输出: Point(1, 2)
## 对象相加时自动调用 __add__
p2 = Point(3, 4)
p3 = p1 + p2 # 自动调用 p1.__add__(p2)
print(p3) # 输出: Point(4, 6)
🏗️ 对象创建和销毁
__new__ - 对象构造
__new__方法负责创建对象实例,在__init__之前调用。
class Person:
def __new__(cls, name):
print(f"创建 {name} 的实例")
# # 必须返回实例对象
return super().__new__(cls)
def __init__(self, name):
print(f"初始化 {name}")
self.name = name
## 创建对象
p = Person("张三")
## 输出:
## 创建 张三 的实例
## 初始化 张三
__init__ - 对象初始化
__init__方法在对象创建后立即调用,用于初始化对象属性。
class Student:
def __init__(self, name, age, grade):
"""初始化学生对象"""
self.name = name
self.age = age
self.grade = grade
print(f"学生 {name} 初始化完成")
## 创建学生对象
student = Student("李四", 18, "高三")
__del__ - 对象销毁
__del__方法在对象被垃圾回收时调用,用于清理资源。
class FileManager:
def __init__(self, filename):
self.filename = filename
self.file = open(filename, 'w')
print(f"打开文件: {filename}")
def __del__(self):
if hasattr(self, 'file') and not self.file.closed:
self.file.close()
print(f"关闭文件: {self.filename}")
## 使用示例
fm = FileManager("test.txt")
del fm # 手动删除,触发 __del__
🎭 对象表示
__str__ - 用户友好的字符串表示
__str__方法定义对象的字符串表示,主要面向最终用户。
class Book:
def __init__(self, title, author, price):
self.title = title
self.author = author
self.price = price
def __str__(self):
return f"《{self.title}》 - {self.author} (¥{self.price})"
book = Book("Python 编程", "张三", 89.9)
print(book) # 输出: 《Python 编程》 - 张三 (¥89.9)
__repr__ - 开发者友好的字符串表示
__repr__方法定义对象的”官方”字符串表示,主要面向开发者。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name} is {self.age} years old"
def __repr__(self):
return f"Person('{self.name}', {self.age})"
p = Person("Alice", 25)
print(str(p)) # 输出: Alice is 25 years old
print(repr(p)) # 输出: Person('Alice', 25)
🔢 数值运算
算术运算符重载
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"Vector({self.x}, {self.y})"
def __add__(self, other):
"""加法运算"""
return Vector(self.x + other.x, self.y + other.y)
def __sub__(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 __truediv__(self, scalar):
"""标量除法"""
return Vector(self.x / scalar, self.y / scalar)
## 使用示例
v1 = Vector(3, 4)
v2 = Vector(1, 2)
print(v1 + v2) # 输出: Vector(4, 6)
print(v1 - v2) # 输出: Vector(2, 2)
print(v1 * 2) # 输出: Vector(6, 8)
print(v1 / 2) # 输出: Vector(1.5, 2.0)
🔍 比较运算
比较运算符重载
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __str__(self):
return f"{self.name}({self.score}分)"
def __eq__(self, other):
"""等于比较"""
return self.score == other.score
def __lt__(self, other):
"""小于比较"""
return self.score < other.score
def __le__(self, other):
"""小于等于比较"""
return self.score <= other.score
def __gt__(self, other):
"""大于比较"""
return self.score > other.score
def __ge__(self, other):
"""大于等于比较"""
return self.score >= other.score
## 使用示例
s1 = Student("张三", 85)
s2 = Student("李四", 92)
print(s1 < s2) # True
print(s1 == s2) # False
print(s2 > s1) # True
## 可以直接排序
students = [s1, s2, Student("王五", 78)]
students.sort()
for student in students:
print(student)
📦 容器行为
__len__ - 长度获取
class Playlist:
def __init__(self):
self.songs = []
def add_song(self, song):
self.songs.append(song)
def __len__(self):
return len(self.songs)
def __str__(self):
return f"播放列表({len(self)}首歌曲)"
playlist = Playlist()
playlist.add_song("歌曲 1")
playlist.add_song("歌曲 2")
print(len(playlist)) # 输出: 2
print(playlist) # 输出: 播放列表(2 首歌曲)
__getitem__ 和 __setitem__ - 索引访问
class Matrix:
def __init__(self, rows, cols):
self.rows = rows
self.cols = cols
self.data = [[0] * cols for _ in range(rows)]
def __getitem__(self, key):
"""获取元素"""
row, col = key
return self.data[row][col]
def __setitem__(self, key, value):
"""设置元素"""
row, col = key
self.data[row][col] = value
def __str__(self):
return '\n'.join([' '.join(map(str, row)) for row in self.data])
## 使用示例
matrix = Matrix(3, 3)
matrix[0, 0] = 1
matrix[1, 1] = 2
matrix[2, 2] = 3
print(matrix[0, 0]) # 输出: 1
print(matrix)
## 输出:
## 1 0 0
## 0 2 0
## 0 0 3
🎯 其他重要魔术方法
__call__ - 可调用对象
class Multiplier:
def __init__(self, factor):
self.factor = factor
def __call__(self, value):
return value * self.factor
## 创建乘法器
double = Multiplier(2)
triple = Multiplier(3)
## 像函数一样调用对象
print(double(5)) # 输出: 10
print(triple(4)) # 输出: 12
__bool__ - 布尔值转换
class BankAccount:
def __init__(self, balance=0):
self.balance = balance
def __bool__(self):
"""账户有余额时返回 True"""
return self.balance > 0
def __str__(self):
return f"账户余额: ¥{self.balance}"
## 使用示例
account1 = BankAccount(100)
account2 = BankAccount(0)
if account1:
print("账户 1 有余额")
if not account2:
print("账户 2 没有余额")
__hash__ - 哈希值计算
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __hash__(self):
return hash((self.name, self.age))
def __eq__(self, other):
if isinstance(other, Person):
return self.name == other.name and self.age == other.age
return False
def __str__(self):
return f"{self.name}({self.age}岁)"
## 使用示例
p1 = Person("张三", 25)
p2 = Person("张三", 25)
p3 = Person("李四", 30)
## 可以作为字典键或集合元素
people_set = {p1, p2, p3} # p1 和 p2 被认为是同一个人
print(f"集合中有 {len(people_set)} 个不同的人") # 输出: 2
🎨 实际应用案例
案例 1:自定义数字类
class Money:
"""货币类,支持各种运算"""
def __init__(self, amount, currency="CNY"):
self.amount = amount
self.currency = currency
def __str__(self):
symbols = {"CNY": "¥", "USD": "$", "EUR": "€"}
symbol = symbols.get(self.currency, self.currency)
return f"{symbol}{self.amount:.2f}"
def __repr__(self):
return f"Money({self.amount}, '{self.currency}')"
def __add__(self, other):
if isinstance(other, Money):
if self.currency != other.currency:
raise ValueError("不能直接相加不同货币")
return Money(self.amount + other.amount, self.currency)
return Money(self.amount + other, self.currency)
def __sub__(self, other):
if isinstance(other, Money):
if self.currency != other.currency:
raise ValueError("不能直接相减不同货币")
return Money(self.amount - other.amount, self.currency)
return Money(self.amount - other, self.currency)
def __mul__(self, factor):
return Money(self.amount * factor, self.currency)
def __truediv__(self, divisor):
return Money(self.amount / divisor, self.currency)
def __eq__(self, other):
return (isinstance(other, Money) and
self.amount == other.amount and
self.currency == other.currency)
def __lt__(self, other):
if isinstance(other, Money) and self.currency == other.currency:
return self.amount < other.amount
raise ValueError("无法比较不同货币")
def __bool__(self):
return self.amount > 0
## 使用示例
price1 = Money(99.99)
price2 = Money(50.00)
print(f"商品 1: {price1}") # 输出: 商品 1: ¥99.99
print(f"商品 2: {price2}") # 输出: 商品 2: ¥50.00
print(f"总价: {price1 + price2}") # 输出: 总价: ¥149.99
print(f"差价: {price1 - price2}") # 输出: 差价: ¥49.99
print(f"打 8 折: {price1 * 0.8}") # 输出: 打 8 折: ¥79.99
案例 2:智能列表类
class SmartList:
"""智能列表,提供额外功能"""
def __init__(self, items=None):
self.items = list(items) if items else []
def __str__(self):
return f"SmartList({self.items})"
def __len__(self):
return len(self.items)
def __getitem__(self, index):
return self.items[index]
def __setitem__(self, index, value):
self.items[index] = value
def __contains__(self, item):
return item in self.items
def __iter__(self):
return iter(self.items)
def __add__(self, other):
if isinstance(other, SmartList):
return SmartList(self.items + other.items)
elif isinstance(other, list):
return SmartList(self.items + other)
else:
return SmartList(self.items + [other])
def __bool__(self):
return len(self.items) > 0
def append(self, item):
self.items.append(item)
def remove(self, item):
self.items.remove(item)
@property
def unique(self):
"""获取去重后的列表"""
return SmartList(list(set(self.items)))
@property
def reversed(self):
"""获取反转后的列表"""
return SmartList(self.items[::-1])
## 使用示例
smart_list = SmartList([1, 2, 3, 2, 4, 3])
print(f"原列表: {smart_list}")
print(f"长度: {len(smart_list)}")
print(f"包含 2: {2 in smart_list}")
print(f"去重: {smart_list.unique}")
print(f"反转: {smart_list.reversed}")
## 列表操作
smart_list += [5, 6]
print(f"添加后: {smart_list}")
📝 最佳实践
1. 魔术方法的一致性
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
if isinstance(other, Point):
return self.x == other.x and self.y == other.y
return False
def __hash__(self):
# # 如果实现了__eq__,通常也要实现__hash__
return hash((self.x, self.y))
def __str__(self):
return f"Point({self.x}, {self.y})"
def __repr__(self):
# # __repr__应该返回可以重新创建对象的字符串
return f"Point({self.x}, {self.y})"
2. 错误处理
class SafeList:
def __init__(self, items=None):
self.items = list(items) if items else []
def __getitem__(self, index):
try:
return self.items[index]
except IndexError:
return None # 返回 None 而不是抛出异常
def __setitem__(self, index, value):
# # 自动扩展列表
while len(self.items) <= index:
self.items.append(None)
self.items[index] = value
3. 性能考虑
class EfficientContainer:
def __init__(self):
self._items = []
self._length = 0 # 缓存长度
def __len__(self):
return self._length # 直接返回缓存的长度
def append(self, item):
self._items.append(item)
self._length += 1 # 更新缓存
def remove(self, item):
self._items.remove(item)
self._length -= 1 # 更新缓存
🔗 扩展阅读
魔术方法是 Python 面向对象编程的强大工具,通过合理使用这些方法,可以让自定义类的行为更加自然和直观。记住要保持方法之间的一致性,并考虑性能和错误处理。