为何大于和不等于运算符在只重载小于和等于运算符时仍然有效?
我现在在研究Python是如何实现运算符重载的。到目前为止,我发现Python的方式比C++更吸引人,尤其是在处理像*
(或者其他类似的算术运算符)时,因为这些运算符可以从右到左(2*x)和从左到右(x*2)进行操作。
我有一个测试用的类:
from math import sqrt
class Vector3:
def __init__(self, x,y,z):
self.x = x
self.y = y
self.z = z
def __repr__(self):
return 'Vector3(x=%d, y=%d, z=%d)' % (self.x, self.y, self.z)
def __str__(self):
return '[x: %d, y: %d, z: %d]' % (self.x, self.y, self.z)
def length(self):
return sqrt(self.x**2 + self.y**2 + self.z**2)
def __add__(self, vector):
return Vector3(self.x + vector.x, self.y + vector.y, self.z + vector.z)
def __sub__(self, vector):
return Vector3(self.x - vector.x, self.y - vector.y, self.z - vector.z)
def __mul__(self, scalar):
return Vector3(self.x * scalar, self.y * scalar, self.z * scalar)
__rmul__ = __mul__ # Right multiplication equals left multiplication (if this defers, __rmul__ has to be overwritten and defined manually)
def __eq__(self, vector):
return (self.x == vector.x and self.y == vector.y and self.z == vector.z)
def __lt__(self, vector):
return self.length() < vector.length()
@staticmethod
def compareAndPrint(vector1, vector2):
if vector1 == vector2: return 'v1 == v2 since len(v1) = %f == %f = len(v2)' % (vector1.length(), vector2.length())
elif vector1 < vector2: return 'v1 < v2 since len(v1) = %f < %f = len(v2)' % (vector1.length(), vector2.length())
elif vector1 > vector2: return 'v1 > v2 since len(v1) = %f > %f = len(v2)' % (vector1.length(), vector2.length())
v1 = Vector3(1,2,3)
v2 = Vector3(0,-1,1)
v3 = v1 + v2
v4 = v3 - v1
v5 = v1 * 2
v6 = 2 * v1
print(v1)
print(v2)
print(v3)
print(v4)
print(v5)
print(v6)
print(Vector3.compareAndPrint(v1,v2))
print(Vector3.compareAndPrint(v2,v1))
print(Vector3.compareAndPrint(v1,v1))
我在我的自定义类中不断添加更多的运算符,并观察它们的表现。你可能注意到了两个事情(根据我标题中的问题):
__gt__
没有被重载,Vector3.compareAndPrint(...)
函数使用了>
(大于)运算符。
出于某种原因,我得到了我预期的输出,就好像我重载了>
一样:
[x: 1, y: 2, z: 3]
[x: 0, y: -1, z: 1]
[x: 1, y: 1, z: 4]
[x: 0, y: -1, z: 1]
[x: 2, y: 4, z: 6]
[x: 2, y: 4, z: 6]
v1 > v2 since len(v1) = 3.741657 > 1.414214 = len(v2)
v1 < v2 since len(v1) = 1.414214 < 3.741657 = len(v2)
v1 == v2 since len(v1) = 3.741657 == 3.741657 = len(v2)
难道Python是自动处理这个问题,还是我做了什么没注意到的事情让它能正常工作?我能想到的唯一一点是,Python可能是取了<
的反向,同时对==
进行了排除,因为>
的反向是<=
,而不仅仅是<
。
同样的情况也适用于!=
(不等于)运算符。在这里我99%确定Python是反转了重载的==
运算符。
相关文章:
- 暂无相关问题
2 个回答
这是关于Python数据模型的一些内容,很多人似乎不太理解。要想弄明白这些,我们需要从二元算术操作开始,比如乘法(__mul__
)、加法(__add__
)等等。
我们注意到有一个 __mul__
方法和一个 __rmul__
方法。它们的区别在文档中有说明,特别是在后者的部分:
这些方法用于实现二元算术操作(比如 +, -, *, /, %, divmod(), pow(), **, <<, >>, &, ^, |),当操作数的顺序被反转时会调用这些方法。只有当左边的操作数不支持对应的操作,并且操作数类型不同的时候,这些函数才会被调用。
接下来,当我们查看丰富比较方法的文档时:
这些方法没有交换参数的版本(用于左边的参数不支持操作但右边的参数支持的情况);相反,
__lt__()
和__gt__()
是彼此的反射。
所以,在你的情况中,由于 __gt__
没有被重载,Python实际上会交换参数的顺序并调用 __lt__
。这真是挺有意思的。
顺便说一下,如果你想构建一个可以与其他同类实例进行排序的类,使用functools.total_ordering
装饰器会非常有帮助。你只需要提供 __lt__
和 __eq__
,然后这个装饰器会帮你处理其他的。
在Python中,大多数二元运算符都可以由任一操作数来重载。左边的操作数可以定义一个方法,比如加法用的__add__
,而右边的操作数则有一个方法,比如__radd__
。我记得唯一只能由一个操作数重载的运算符是in
,它必须由右边的操作数来定义。
对于比较操作,__gt__
和__rgt__
这两个方法其实有点不同,__rgt__
实际上就是__lt__
。这意味着当你写left_thing > right_thing
时,如果left_thing
不知道该怎么做,Python会尝试用right_thing < left_thing
来解决。只要你实现了__lt__
,这个方法就能正常工作。
需要注意的是,如果__gt__
和__lt__
都失败了,Python不会尝试使用__le__
、__ge__
或__eq__
这些方法。