第65集:常用魔术方法

学习目标

  1. 掌握常用魔术方法的作用和使用场景
  2. 学会实现运算符重载功能
  3. 理解比较运算、算术运算的魔术方法
  4. 能够应用魔术方法提升类的易用性

魔术方法概念

什么是魔术方法

魔术方法(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

课后练习

  1. 创建一个 ComplexNumber 类(复数),实现加减乘除运算
  2. 实现一个 Rectangle 类,支持面积比较(>、<、==)
  3. 创建一个 ShoppingCart 类,实现商品数量的 len() 和包含检测
  4. 设计一个 Temperature 类,支持不同温度单位的转换和比较
  5. 实现一个 Matrix 类的基础版本,支持矩阵加法

总结

常用魔术方法让我们能够:

  • 实现运算符重载,让自定义类支持数学运算
  • 定义对象的比较行为,支持排序和比较操作
  • 增强容器的功能,支持 len()、in 等操作
  • 提升代码的可读性和易用性

这些方法是Python面向对象编程的高级特性,合理使用可以让代码更加优雅和直观。

« 上一篇 魔术方法基础 下一篇 » 枚举类型