# Python 运算符重载

> 原文： [https://www.programiz.com/python-programming/operator-overloading](https://www.programiz.com/python-programming/operator-overloading)

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

## Python 运算符重载

[Python 运算符](/python-programming/operators)用于内置类。 但是同一运算符对于不同类型的行为会有所不同。 例如，`+`运算符将对两个数字执行算术加法，合并两个列表或连接两个字符串。

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

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

```py
class Point:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

p1 = Point(1, 2)
p2 = Point(2, 3)
print(p1+p2)
```

**输出**

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

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

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

* * *

## Python 特殊函数

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

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

Python 中还有许多其他特殊函数。 访问 [Python 特殊函数](http://docs.python.org/3/reference/datamodel.html#special-method-names)了解有关它们的更多信息。

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

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

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

```py
class Point:
    def __init__(self, x = 0, y = 0):
        self.x = x
        self.y = y

    def __str__(self):
        return "({0},{1})".format(self.x,self.y)
```

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

```py
class Point:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __str__(self):
        return "({0}, {1})".format(self.x, self.y)

p1 = Point(2, 3)
print(p1)
```

**输出**：

```py
(2, 3)
```

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

```py
>>> str(p1)
'(2,3)'

>>> format(p1)
'(2,3)'
```

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

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

* * *

## 重载`+`运算符

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

```py
class Point:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __str__(self):
        return "({0},{1})".format(self.x, self.y)

    def __add__(self, other):
        x = self.x + other.x
        y = self.y + other.y
        return Point(x, y)
```

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

```py
class Point:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __str__(self):
        return "({0},{1})".format(self.x, self.y)

    def __add__(self, other):
        x = self.x + other.x
        y = self.y + other.y
        return Point(x, y)

p1 = Point(1, 2)
p2 = Point(2, 3)

print(p1+p2)
```

**输出**：

```py
(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`类中实现小于符号`<`符号。

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

```py
# overloading the less than operator
class Point:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __str__(self):
        return "({0},{1})".format(self.x, self.y)

    def __lt__(self, other):
        self_mag = (self.x ** 2) + (self.y ** 2)
        other_mag = (other.x ** 2) + (other.y ** 2)
        return self_mag < other_mag

p1 = Point(1,1)
p2 = Point(-2,-3)
p3 = Point(1,-1)

# use less than
print(p1<p2)
print(p2<p3)
print(p1<p3)
```

**输出**：

```py
True
False
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)` |