原文: https://www.programiz.com/python-programming/operator-overloading

您可以根据所使用的操作数来更改 Python 中运算符的含义。 在本教程中,您将学习如何在 Python 面向对象编程中使用运算符重载。

Python 运算符重载

Python 运算符用于内置类。 但是同一运算符对于不同类型的行为会有所不同。 例如,+运算符将对两个数字执行算术加法,合并两个列表或连接两个字符串。

Python 中的这种功能允许同一运算符根据上下文具有不同的含义,这称为运算符重载。

那么,当我们将它们与用户定义类的对象一起使用时会发生什么呢? 让我们考虑下面的类,它试图在二维坐标系中模拟一个点。

  1. class Point:
  2. def __init__(self, x=0, y=0):
  3. self.x = x
  4. self.y = y
  5. p1 = Point(1, 2)
  6. p2 = Point(2, 3)
  7. print(p1+p2)

输出

  1. Traceback (most recent call last):
  2. File "<string>", line 9, in <module>
  3. print(p1+p2)
  4. TypeError: unsupported operand type(s) for +: 'Point' and 'Point'

在这里,我们可以看到出现了TypeError,因为 Python 不知道如何将两个Point对象加在一起。

但是,我们可以通过运算符重载在 Python 中完成此任务。 但是首先,让我们对特殊函数有所了解。


Python 特殊函数

以双下划线__开头的类函数在 Python 中称为特殊函数。

这些函数不是我们为类定义的典型函数。 我们上面定义的__init__()函数就是其中之一。 每次我们创建该类的新对象时都会调用它。

Python 中还有许多其他特殊函数。 访问 Python 特殊函数了解有关它们的更多信息。

使用特殊函数,我们可以使我们的类与内置函数兼容。

  1. >>> p1 = Point(2,3)
  2. >>> print(p1)
  3. <__main__.Point object at 0x00000000031F8CC0>

假设我们希望print()函数打印Point对象的坐标,而不是所得到的。 我们可以在我们的类中定义__str__()方法,该方法控制对象的打印方式。 让我们看看如何实现这一目标:

  1. class Point:
  2. def __init__(self, x = 0, y = 0):
  3. self.x = x
  4. self.y = y
  5. def __str__(self):
  6. return "({0},{1})".format(self.x,self.y)

现在,让我们再次尝试print()函数。

  1. class Point:
  2. def __init__(self, x=0, y=0):
  3. self.x = x
  4. self.y = y
  5. def __str__(self):
  6. return "({0}, {1})".format(self.x, self.y)
  7. p1 = Point(2, 3)
  8. print(p1)

输出

  1. (2, 3)

这样更好,事实证明,当我们使用内置函数str()format()时,将调用相同的方法。

  1. >>> str(p1)
  2. '(2,3)'
  3. >>> format(p1)
  4. '(2,3)'

因此,当您使用str(p1)format(p1)时,Python 内部会调用p1.__str__()方法。 因此得名,特殊函数。

现在让我们回到操作符重载。


重载+运算符

要重载+运算符,我们将需要在该类中实现__add__()函数。 拥有权利的同时也被赋予了重大的责任。 我们可以在此函数内做任何喜欢的事情。 但是返回坐标和的Point对象更为明智。

  1. class Point:
  2. def __init__(self, x=0, y=0):
  3. self.x = x
  4. self.y = y
  5. def __str__(self):
  6. return "({0},{1})".format(self.x, self.y)
  7. def __add__(self, other):
  8. x = self.x + other.x
  9. y = self.y + other.y
  10. return Point(x, y)

现在让我们再次尝试加法操作:

  1. class Point:
  2. def __init__(self, x=0, y=0):
  3. self.x = x
  4. self.y = y
  5. def __str__(self):
  6. return "({0},{1})".format(self.x, self.y)
  7. def __add__(self, other):
  8. x = self.x + other.x
  9. y = self.y + other.y
  10. return Point(x, y)
  11. p1 = Point(1, 2)
  12. p2 = Point(2, 3)
  13. print(p1+p2)

输出

  1. (3,5)

实际发生的是,当您使用p1 + p2时,Python 会调用p1.__add__(p2),而后者依次为Point.__add__(p1,p2)。 此后,将按照我们指定的方式执行加法运算。

同样,我们也可以重载其他运算符。 我们需要实现的特殊函数列表如下。

运算符 表达式 内部
加成 p1 + p2 p1.__add__(p2)
减法 p1 - p2 p1.__sub__(p2)
乘法 p1 * p2 p1.__mul__(p2)
p1 ** p2 p1.__pow__(p2)
除法 p1 / p2 p1.__truediv__(p2)
整数除法 p1 // p2 p1.__floordiv__(p2)
余数(模) p1 % p2 p1.__mod__(p2)
按位左移 p1 << p2 p1.__lshift__(p2)
按位右移 p1 >> p2 p1.__rshift__(p2)
按位与 p1 & p2 p1.__and__(p2)
按位或 p1 &#124; p2 p1.__or__(p2)
按位异或 p1 ^ p2 p1.__xor__(p2)
按位非 ~p1 p1.__invert__()

重载比较运算符

Python 不仅将运算符重载不限于算术运算符。 我们也可以重载比较运算符。

假设我们想在我们的Point类中实现小于符号<符号。

让我们从原点比较这些点的大小,并为此目的返回结果。 可以如下实现。

  1. # overloading the less than operator
  2. class Point:
  3. def __init__(self, x=0, y=0):
  4. self.x = x
  5. self.y = y
  6. def __str__(self):
  7. return "({0},{1})".format(self.x, self.y)
  8. def __lt__(self, other):
  9. self_mag = (self.x ** 2) + (self.y ** 2)
  10. other_mag = (other.x ** 2) + (other.y ** 2)
  11. return self_mag < other_mag
  12. p1 = Point(1,1)
  13. p2 = Point(-2,-3)
  14. p3 = Point(1,-1)
  15. # use less than
  16. print(p1<p2)
  17. print(p2<p3)
  18. print(p1<p3)

输出

  1. True
  2. False
  3. False

类似地,下面列出了我们需要实现以重载其他比较运算符的特殊函数。

运算符 表达式 内部
小于 p1 < p2 p1.__lt__(p2)
小于或等于 p1 <= p2 p1.__le__(p2)
等于 p1 == p2 p1.__eq__(p2)
不等于 p1 != p2 p1.__ne__(p2)
大于 p1 > p2 p1.__gt__(p2)
大于或等于 p1 >= p2 p1.__ge__(p2)