运算符魔法方法通常用于实现对象的运算、赋值等操作。
前言中介绍的 __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魔法方法漫游指南
看完文章想吐槽?欢迎留言告诉我!