运算符魔法方法通常用于实现对象的运算、赋值等操作。
前言中介绍的 __add__ 就属于运算符魔法方法。让我们继续探讨。
使用 Python 魔法方法的一个巨大的好处,就是可以构建与内置类型相似行为的对象。
什么意思?比方说在某些语言中,要验证两个自定义对象是否相等,代码可能是这样:
if obj.isEqual(other):
# ...Python 中同样可以这样实现,但缺点是它是非标准的,你可能需要稍微花一点时间才知道它要做什么。另外,不同的程序员可能对函数的名称理解不同,从而使得同一个功能具有 equals() 、 isEqual() 、 equalTo() 等不同的名称。
让我们看看如果运用了魔法方法:
if obj == other:
# ...是不是一目了然?
要实现 == 运算符,只需要你实现 __eq__ 方法:
class Foo:
def __init__(self, id):
self.id = id
def __eq__(self, other):
return self.id == other.id
f1 = Foo(1)
f2 = Foo(1)
f3 = Foo(2)
print(f1 == f2, f2 == f3)
# 输出:
# True False这就是魔法的力量。
类似的比较运算符还有:
__lt____le____ne____gt____ge__
分别实现 < 、 <= 、 != 、 > 、 >= 运算符。
算数运算符用于常见的数学运算,如 + - * / 加减乘除等。
比如 __add__ 魔法方法可以实现加法操作。
整数相加的计算就相当于:
>>> num = 1
>>> num + 2
3
>>> num.__add__(2)
3你可以让一个自定义的类实现 __add__ 魔法方法,从而使它也可以进行加法计算。
比如定义一个矢量:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
new_x = self.x + other.x
new_y = self.y + other.y
return Vector(new_x, new_y)由于实现了 __add__ 方法,这个矢量类就可以非常自然的相加:
>>> v1 = Vector(1, 2)
>>> v2 = Vector(3, 4)
>>> v3 = v1 + v2
>>> v3.x
4
>>> v3.y
6类似的,我们就可以实现 Vector 类的整套加减乘除运算了:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
# 加
def __add__(self, other):
new_x = self.x + other.x
new_y = self.y + other.y
return Vector(new_x, new_y)
# 减
def __sub__(self, other):
new_x = self.x - other.x
new_y = self.y - other.y
return Vector(new_x, new_y)
# 乘
def __mul__(self, other):
new_x = self.x * other.x
new_y = self.y * other.y
return Vector(new_x, new_y)
# 除
def __truediv__(self, other):
new_x = self.x / other.x
new_y = self.y / other.y
return Vector(new_x, new_y)类似的算数运算符还有:
__matmul____floordiv____mod____pow____lshift____rshift____and____xor____or__
以上魔法方法分别实现 @ ,// ,% ,** ,<< ,>> , & , ^ , | 操作。
了解这些方法的功能即可。需要用到时稍加搜索,就能知道其需要的参数。
除此之外还有个 __divmod__ 方法,它把除数和余数运算结果结合起来,返回一个包含商和余数的元组,也就是把 __floordiv__ 和 __mod__ 的作用融合在一起了。
上面探讨的算数运算符要求位于运算符前面的对象实现,比如:
first + second这个式子中,要求 first 必须实现 __add__ 方法。
但是如果你没办法保证 first 的具体实现,那么也可以在 second 实现反算数运算符 __radd__,达到同样的效果。
应用场景举例:
second是库提供的,而first是用户自行编写,没办法保证其能够实现__add__。
反算数运算的名称就是在正常算数运算符前面加字母 r,比如 __radd__ 、 __rsub__,就不展开讲了。
与反算数运算符类似的还有增量赋值符,比如最常用的 +=:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __iadd__(self, other):
return Vector(self.x + other, self.y + other)
v1 = Vector(-1, 2)
v1 += 1
print(v1.x, v1.y)
# 输出:
# 0 3其他的增量赋值符的还有 __isub__ 、 __imul__ 等,也不展开列举了。
一元运算符只有一个操作符。
还是拿上面的 Vector 类举例,比方说我要实现取负的操作:
class Vector:
def __neg__(self):
return Vector(-self.x, -self.y)
# 其他方法
# ...那么就可以这样取负了:
>>> v1 = Vector(-1, 2)
>>> v2 = -v1
>>> print(v2.x, v2.y)
1 -2类似的一元运算符还有:
__pos__:取正,比如+v1。__abs__:取绝对值,如abs(v1)。__invert__:实现取反操作符~,二进制逐位取反。__complex__:实现内建函数complex(),取复数。__int__:实现内建函数int(),整型转换。__float__:实现内建函数float(),浮点数转换。__round__:实现内建函数round(),四舍五入。__ceil__:实现内建函数math.ceil(),大于原始值的最小整数。__floor__:实现内建函数math.floor(),小于原始值的最大整数__trunc__:实现内建函数math.trunc(),朝零取整__index__:作为列表索引的数字。
稍微难理解的只有最后这个 __index__,用一个例子说明:
class Index:
def __index__(self):
return 1
my_list = [0, 1, 2]
index = Index()
print(my_list[index])
# 输出:
# 1以上就是常见运算符魔法方法的用法了。
可能你觉得实现个加减乘除没多大用处。别急,这只是魔法方法中非常小的一部分。让我们下一章继续深入。
本系列文章开源发布于 Github,传送门:Python魔法方法漫游指南
看完文章想吐槽?欢迎留言告诉我!