第65集:常用魔术方法
学习目标
- 掌握常用魔术方法的作用和使用场景
- 学会实现运算符重载功能
- 理解比较运算、算术运算的魔术方法
- 能够应用魔术方法提升类的易用性
魔术方法概念
什么是魔术方法
魔术方法(Magic Methods)是Python中以双下划线包围的特殊方法,如 __init__、__str__ 等。它们允许我们在自定义类中定义特定操作的行为。
运算符重载原理
通过实现特定的魔术方法,我们可以让自定义类支持Python内置运算符,使对象可以像内置类型一样参与运算。
常用魔术方法详解
算术运算魔术方法
__add__(self, other) - 加法运算
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 __str__(self):
return f"Vector({self.x}, {self.y})"
# 使用示例
v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2 # 调用 v1.__add__(v2)
print(v3) # 输出: Vector(4, 6)__sub__(self, other) - 减法运算
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __sub__(self, other):
"""实现向量减法"""
return Vector(self.x - other.x, self.y - other.y)
# 使用示例
v1 = Vector(5, 5)
v2 = Vector(2, 3)
v3 = v1 - v2
print(v3) # 输出: Vector(3, 2)__mul__(self, other) - 乘法运算
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __mul__(self, scalar):
"""实现向量数乘"""
if isinstance(scalar, (int, float)):
return Vector(self.x * scalar, self.y * scalar)
raise TypeError("只能与数字相乘")
# 使用示例
v = Vector(2, 3)
result = v * 3
print(result) # 输出: Vector(6, 9)__truediv__(self, other) - 真除法运算
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __truediv__(self, scalar):
"""实现向量除法"""
if isinstance(scalar, (int, float)) and scalar != 0:
return Vector(self.x / scalar, self.y / scalar)
raise ValueError("除数不能为零")
# 使用示例
v = Vector(6, 9)
result = v / 3
print(result) # 输出: Vector(2.0, 3.0)反向算术运算
当左操作数不支持相应运算时,Python会尝试调用右操作数的反向方法:
__radd__(self, other) - 反向加法
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
if isinstance(other, Vector):
return Vector(self.x + other.x, self.y + other.y)
elif isinstance(other, (int, float)):
return Vector(self.x + other, self.y + other)
return NotImplemented
def __radd__(self, other):
"""反向加法:other + self"""
return self.__add__(other) # 加法满足交换律
# 使用示例
v = Vector(1, 2)
result1 = v + 10 # Vector(11, 12)
result2 = 10 + v # Vector(11, 12),调用 __radd__比较运算魔术方法
__eq__(self, other) - 相等比较
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
"""判断两个点是否相等"""
if not isinstance(other, Point):
return False
return self.x == other.x and self.y == other.y
# 使用示例
p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = Point(3, 4)
print(p1 == p2) # 输出: True
print(p1 == p3) # 输出: False
print(p1 == "hello") # 输出: False__ne__(self, other) - 不等比较
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
if not isinstance(other, Point):
return False
return self.x == other.x and self.y == other.y
def __ne__(self, other):
"""判断两个点是否不相等"""
eq_result = self.__eq__(other)
if eq_result is NotImplemented:
return NotImplemented
return not eq_result
# 使用示例
p1 = Point(1, 2)
p2 = Point(3, 4)
print(p1 != p2) # 输出: True__lt__(self, other) - 小于比较
class Score:
def __init__(self, value):
self.value = value
def __lt__(self, other):
"""小于比较"""
if isinstance(other, Score):
return self.value < other.value
elif isinstance(other, (int, float)):
return self.value < other
return NotImplemented
# 使用示例
s1 = Score(85)
s2 = Score(92)
print(s1 < s2) # 输出: True
print(s1 < 90) # 输出: True__le__(self, other) - 小于等于比较
class Score:
def __init__(self, value):
self.value = value
def __le__(self, other):
"""小于等于比较"""
if isinstance(other, Score):
return self.value <= other.value
elif isinstance(other, (int, float)):
return self.value <= other
return NotImplemented__gt__(self, other) - 大于比较
__ge__(self, other) - 大于等于比较
容器类魔术方法
__contains__(self, item) - 成员检测
class MyList:
def __init__(self, data):
self.data = data
def __contains__(self, item):
"""实现 in 操作符"""
return item in self.data
# 使用示例
my_list = MyList([1, 2, 3, 4, 5])
print(3 in my_list) # 输出: True
print(10 in my_list) # 输出: False__len__(self) - 长度获取
class Stack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop() if self.items else None
def __len__(self):
"""返回栈的大小"""
return len(self.items)
# 使用示例
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print(len(stack)) # 输出: 3应用案例
案例1:货币类实现算术运算
class Money:
def __init__(self, amount, currency="CNY"):
self.amount = amount
self.currency = currency
def __add__(self, other):
if self.currency != other.currency:
raise ValueError("不能相加不同货币的金钱")
return Money(self.amount + other.amount, self.currency)
def __sub__(self, other):
if self.currency != other.currency:
raise ValueError("不能相减不同货币的金钱")
return Money(self.amount - other.amount, self.currency)
def __mul__(self, multiplier):
if isinstance(multiplier, (int, float)):
return Money(self.amount * multiplier, self.currency)
raise TypeError("乘数必须是数字")
def __eq__(self, other):
if not isinstance(other, Money):
return False
return self.amount == other.amount and self.currency == other.currency
def __str__(self):
return f"{self.amount} {self.currency}"
# 使用示例
salary1 = Money(5000, "CNY")
salary2 = Money(3000, "CNY")
total = salary1 + salary2
bonus = salary1 * 0.1
print(f"总工资: {total}") # 输出: 总工资: 8000 CNY
print(f"奖金: {bonus}") # 输出: 奖金: 500.0 CNY案例2:学生成绩管理系统
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __eq__(self, other):
if not isinstance(other, Student):
return False
return self.name == other.name
def __lt__(self, other):
if not isinstance(other, Student):
return NotImplemented
return self.score < other.score
def __str__(self):
return f"{self.name}: {self.score}分"
class ClassRoom:
def __init__(self):
self.students = []
def add_student(self, student):
self.students.append(student)
def __contains__(self, student):
return student in self.students
def get_top_student(self):
return max(self.students) if self.students else None
# 使用示例
classroom = ClassRoom()
classroom.add_student(Student("张三", 85))
classroom.add_student(Student("李四", 92))
classroom.add_student(Student("王五", 78))
print("最高分学生:", classroom.get_top_student())
print("张三在班级中:", Student("张三", 85) in classroom)常见错误
错误1:忘记返回结果
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
# 错误:没有返回结果
Vector(self.x + other.x, self.y + other.y)
# 正确:应该返回结果
# return Vector(self.x + other.x, self.y + other.y)
v1 = Vector(1, 2)
v2 = Vector(3, 4)
result = v1 + v2
print(result) # 输出: None错误2:没有处理类型检查
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)
# 可能导致 AttributeError
v = Vector(1, 2)
# result = v + "hello" # 会抛出 AttributeError错误3:返回 NotImplemented 不当
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
if isinstance(other, Vector):
return Vector(self.x + other.x, self.y + other.y)
# 错误:返回 False 而不是 NotImplemented
# return False
# 正确:返回 NotImplemented 让Python尝试其他方法
return NotImplemented课后练习
- 创建一个
ComplexNumber类(复数),实现加减乘除运算 - 实现一个
Rectangle类,支持面积比较(>、<、==) - 创建一个
ShoppingCart类,实现商品数量的 len() 和包含检测 - 设计一个
Temperature类,支持不同温度单位的转换和比较 - 实现一个
Matrix类的基础版本,支持矩阵加法
总结
常用魔术方法让我们能够:
- 实现运算符重载,让自定义类支持数学运算
- 定义对象的比较行为,支持排序和比较操作
- 增强容器的功能,支持 len()、in 等操作
- 提升代码的可读性和易用性
这些方法是Python面向对象编程的高级特性,合理使用可以让代码更加优雅和直观。