为什么大于和不等于运算符可以工作,即使只有小于和等于运算符被重载
Why does greater than and unequal operators work even though only less than and equal operator has been overloaded
我目前正在研究 Python 如何重载其运算符。到目前为止,我发现它比 C++ 更具吸引力,尤其是在运算符方面,例如 *
(或类似的算术运算符)必须处理可以从右到左 (2*x) 和左应用的运算以某种方式向右 (x*2)。
我有以下 class 作为测试:
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))
我只是在我的自定义 class 中添加越来越多的运算符并观察它们的行为方式。您可能已经注意到两件事(根据我在标题中的问题):
__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 会反转重载的 ==
运算符。
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__
的操作。
这是许多人似乎不理解的 python 数据模型的一部分。要通过文档追踪这一点,我们需要从二进制算术运算(__mul__
、__add__
等)开始。
我们注意到有一个 __mul__
and a __rmul__
method. The difference is described in docs under the latter:
These methods are called to implement the binary arithmetic operations (+, -, *, /, %, divmod(), pow(), **, <<, >>, &, ^, |) with reflected (swapped) operands. These functions are only called if the left operand does not support the corresponding operation and the operands are of different types.
现在,当我们查看 rich comparison methods 的文档时:
There are no swapped-argument versions of these methods (to be used when the left argument does not support the operation but the right argument does); rather, __lt__()
and __gt__()
are each other’s reflection
所以,你的情况是因为 __gt__
没有被重载,python 实际上交换了参数的顺序并调用了 __lt__
。很整洁。
FWIW,如果你想构建一个可与 class 的其他实例一起订购的 class,functools.total_ordering
装饰器可以是 super 有帮助。您只需提供 __lt__
和 __eq__
,装饰器提供其余部分。
我目前正在研究 Python 如何重载其运算符。到目前为止,我发现它比 C++ 更具吸引力,尤其是在运算符方面,例如 *
(或类似的算术运算符)必须处理可以从右到左 (2*x) 和左应用的运算以某种方式向右 (x*2)。
我有以下 class 作为测试:
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))
我只是在我的自定义 class 中添加越来越多的运算符并观察它们的行为方式。您可能已经注意到两件事(根据我在标题中的问题):
__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 会反转重载的 ==
运算符。
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__
的操作。
这是许多人似乎不理解的 python 数据模型的一部分。要通过文档追踪这一点,我们需要从二进制算术运算(__mul__
、__add__
等)开始。
我们注意到有一个 __mul__
and a __rmul__
method. The difference is described in docs under the latter:
These methods are called to implement the binary arithmetic operations (+, -, *, /, %, divmod(), pow(), **, <<, >>, &, ^, |) with reflected (swapped) operands. These functions are only called if the left operand does not support the corresponding operation and the operands are of different types.
现在,当我们查看 rich comparison methods 的文档时:
There are no swapped-argument versions of these methods (to be used when the left argument does not support the operation but the right argument does); rather,
__lt__()
and__gt__()
are each other’s reflection
所以,你的情况是因为 __gt__
没有被重载,python 实际上交换了参数的顺序并调用了 __lt__
。很整洁。
FWIW,如果你想构建一个可与 class 的其他实例一起订购的 class,functools.total_ordering
装饰器可以是 super 有帮助。您只需提供 __lt__
和 __eq__
,装饰器提供其余部分。