CodeGym /课程 /Python SELF ZH /运算符重载

运算符重载

Python SELF ZH
第 20 级 , 课程 0
可用

6.1 魔术方法

Python 中的运算符重载允许为用户自定义类定义或改变内置运算符(比如 +, -, *, /)的行为。 这是通过一些特殊的方法实现的,这些方法也被称为魔术方法。

例如,你可以在自己的类中重载比较运算符:

运算符 无下划线方法 方法签名
== eq() __eq__(self, other)
!= ne() __ne__(self, other)
< lt() __lt__(self, other)
<= le() __le__(self, other)
> gt() __gt__(self, other)
>= ge() __ge__(self, other)

假设你写了一个类,并希望你的类的对象能够按你所需的方式进行比较。你只需要在类中实现 «__eq__» 方法,Python 会在每次代码中比较你的类的对象时调用这个方法。

示例:


class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y
        
# 使用
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1 == v2)  # 输出:True
print(v1 == v3)  # 输出:False

每次你比较两个对象时,Python 会检查它们是否实现了 函数 «__eq__»。如果有,就调用这个方法。如果没有,就直接比较对象的引用。

事实上,上例中的意思是(只需要添加对方法存在的检查):


# 使用
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1.__eq__(v2))  # 输出:True
print(v1.__eq__(v3))  # 输出:False

6.2 所有运算符列表

总共有 6 组运算符可以重载。

算术运算符:

运算符 无下划线方法 方法签名
+ add __add__(self, other)
- sub __sub__(self, other)
* mul __mul__(self, other)
/ truediv __truediv__(self, other)
// floordiv __floordiv__(self, other)
% mod __mod__(self, other)
** pow __pow__(self, other)

比较运算符:

运算符 无下划线方法 方法签名
== eq __eq__(self, other)
!= ne __ne__(self, other)
< lt __lt__(self, other)
<= le __le__(self, other)
> gt __gt__(self, other)
>= ge __ge__(self, other)

逻辑运算符:

运算符 无下划线方法 方法签名
& and __and__(self, other)
| or __or__(self, other)
^ xor __xor__(self, other)
~ invert __invert__(self)

索引和切片运算符:

运算符 方法
obj[key] __getitem__(self, key)
obj[key] = value __setitem__(self, key, value)
del obj[key] __delitem__(self, key)

一元运算符:

运算符 方法
- __neg__(self)
+ __pos__(self)
abs() __abs__(self)
~ __invert__(self)

赋值运算符:

运算符 方法
+= __iadd__(self, other)
-= __isub__(self, other)
*= __imul__(self, other)
/= __itruediv__(self, other)
//= __ifloordiv__(self, other)
%= __imod__(self, other)
**= __ipow__(self, other)

可能这就是 Python 比较慢的原因——每次执行运算符前,它会在类及其所有父类中寻找相应的方法。 不过,这让我们可以写出世界上最紧凑的代码 :)

6.3 索引运算符

可以比较对象或者减去集合,在某种程度上是显而易见的。 如果你编写一个类,并且考虑到其上的逻辑或数学运算,你自己会想到这一点。

我想和你们探讨一个有趣的例子——索引运算符。让我们从代码示例开始:


class CustomList:
    def __init__(self, data):
        self.data = data
        
    def __getitem__(self, index):
        return self.data[index]
        
    def __setitem__(self, index, value):
        self.data[index] = value
        
    def __delitem__(self, index):
        del self.data[index]
        
    def __repr__(self):
        return repr(self.data)
        
# 使用
c_list = CustomList([1, 2, 3, 4, 5])
print(c_list[1])  # 输出:2
c_list[1] = 10
print(c_list)  # 输出:[1, 10, 3, 4, 5]
del c_list[1]
print(c_list)  # 输出:[1, 3, 4, 5]

这里我们看到三个操作的示例:

  • 通过索引读取数据
  • 通过索引写入数据
  • 甚至通过索引删除数据。

数据也不一定必须是以列表形式存储的。或者索引也不一定必须是数字。例如,dictionary(字典)类就是这样实现的。

创建 SuperList

还记得 list 类吗?它可以分配元素,但只能使用已经存在的索引。让我们创建一个自己的类,叫做 SuperList,你可以用任意索引来访问它的元素:

  • 如果索引 < 0,将元素插入到开头
  • 如果索引 >= len,添加元素到末尾
  • 其他情况下只返回元素

示例:


class SuperList(list):
    def __init__(self, value):
        super().__init__(value)
        
    def __setitem__(self, index, value):
        if index >= len(self):
            super().append(value)
        elif index < 0:
            super().insert(0, value)
        else:
            super().__setitem__(index, value)
        
lst = SuperList([1, 2, 3])
lst[200] = 100
lst[-200] = 99
print(lst)  # [99, 1, 2, 3, 100]

所以索引重载是一个很棒的功能,我推荐你在实践中使用它。今天就先这样吧。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION