为什么 Python 没有“__req__”(反映相等性)方法?
Why doesn't Python have a "__req__" (reflected equality) method?
我有个小帮手class:
class AnyOf(object):
def __init__(self, *args):
self.elements = args
def __eq__(self, other):
return other in self.elements
这让我可以像这样施展甜蜜魔法:
>>> arr = np.array([1,2,3,4,5])
>>> arr == AnyOf(2,3)
np.array([False, True, True, False, False])
无需使用列表理解(如 np.array(x in (2,3) for x in arr
)。
(我维护了一个 UI 让(受信任的)用户输入任意代码,并且 a == AnyOf(1,2,3)
比非技术通晓的用户的列表理解更容易接受。)
但是!
这只有一种方法!例如,如果我要执行 AnyOf(2,3) == arr
,那么我的 AnyOf
class 的 __eq__
方法永远不会被调用:相反,NumPy 数组的 __eq__
方法被调用,它在内部(我假设)调用其所有元素的 __eq__
方法。
这让我想知道:为什么 Python 不允许右侧等价于 __eq__
? (大致等同于 __radd__
、__rmul__
等方法。)
documentation 关于像 __radd__
这样的 __rxx__
方法指出:
These functions are only called if the left operand does not support the
corresponding operation and the operands are of different types.
虽然 classes 默认没有 __add__
或 __sub__
方法,但它们 有 有 __eq__
:
>>> class A(object):
... pass
>>> '__eq__' in dir(A)
True
这意味着 __req__
将永远不会被调用,除非您明确地从另一个 class 中删除 __eq__
或使 __eq__
return NotImplemented
.
您可以使用 np.in1d
解决您的特定问题:
>>> np.in1d(arr, [2, 3])
array([False, True, True, False, False], dtype=bool)
这是关于 data model 的文档:
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, __le__()
and __ge__()
are each other’s reflection, and
__eq__()
and __ne__()
are their own reflection. If the operands are of different types, and right operand’s type is a direct or indirect
subclass of the left operand’s type, the reflected method of the right
operand has priority, otherwise the left operand’s method has
priority. Virtual subclassing is not considered.
正如上面的评论所述,你想要的是什么,__eq__
本质上与潜在的 __req__
相同:它在 ==
的右侧调用如果左侧的对象 returns NotImplemented
:
In [1]: class A:
...: def __eq__(self, other):
...: return NotImplemented
...:
In [2]: class B:
...: def __eq__(self, other):
...: print("B comparing")
...: return True
...:
In [3]: B() == A()
B comparing
Out[3]: True
In [4]: A() == B()
B comparing
Out[4]: True
In [5]: A() == A()
Out[5]: False
它甚至可以与其他普通对象一起使用:
In [10]: 5 == B()
B comparing
Out[10]: True
但是,某些对象可能会在 __eq__
上产生 TypeError 而不是返回 NotImplemented
或 False
,这使得这对所有类型的对象都不可靠。
在您的案例中发生的情况是在您自己的 __eq__
方法中对数组和元组错误地使用了运算符 in
。 (感谢@wim 在这里的另一个答案中发现了这一点)。
__req__
在语言中不是一个好主意,因为如果 class Left
定义 __eq__
而 class Right
定义__req__
,那么 Python 必须就 Left() == Right()
中谁先被调用做出一致的决定。他们不可能都赢。
但是,Python 数据模型确实允许您在这里做您想做的事情。您可以从两侧控制此比较,但您需要正确定义 AnyOf
。 如果您希望AnyOf
从右侧控制__eq__,您必须将其定义为np.ndarray
的子class。
if I were to do AnyOf(2,3) == arr
then my AnyOf
class's __eq__
method never gets called
不对,你这里有一个根本性的误解。左侧总是首先尝试相等比较,除非右侧是左侧类型的子class。
arr == AnyOf(2,3)
在上面的例子中,你的自定义 __eq__
被调用,因为 numpy 数组调用它!所以 np.ndarray
获胜,它决定每个元素检查一次。它实际上可以做任何其他事情,包括根本不调用您的 AnyOf.__eq__
。
AnyOf(2,3) == arr
在上面的例子中,你的 class 确实在比较中得到了第一次尝试,但由于你使用 in
的方式(检查数组是否在元组中)而失败了。
我有个小帮手class:
class AnyOf(object):
def __init__(self, *args):
self.elements = args
def __eq__(self, other):
return other in self.elements
这让我可以像这样施展甜蜜魔法:
>>> arr = np.array([1,2,3,4,5])
>>> arr == AnyOf(2,3)
np.array([False, True, True, False, False])
无需使用列表理解(如 np.array(x in (2,3) for x in arr
)。
(我维护了一个 UI 让(受信任的)用户输入任意代码,并且 a == AnyOf(1,2,3)
比非技术通晓的用户的列表理解更容易接受。)
但是!
这只有一种方法!例如,如果我要执行 AnyOf(2,3) == arr
,那么我的 AnyOf
class 的 __eq__
方法永远不会被调用:相反,NumPy 数组的 __eq__
方法被调用,它在内部(我假设)调用其所有元素的 __eq__
方法。
这让我想知道:为什么 Python 不允许右侧等价于 __eq__
? (大致等同于 __radd__
、__rmul__
等方法。)
documentation 关于像 __radd__
这样的 __rxx__
方法指出:
These functions are only called if the left operand does not support the corresponding operation and the operands are of different types.
虽然 classes 默认没有 __add__
或 __sub__
方法,但它们 有 有 __eq__
:
>>> class A(object):
... pass
>>> '__eq__' in dir(A)
True
这意味着 __req__
将永远不会被调用,除非您明确地从另一个 class 中删除 __eq__
或使 __eq__
return NotImplemented
.
您可以使用 np.in1d
解决您的特定问题:
>>> np.in1d(arr, [2, 3])
array([False, True, True, False, False], dtype=bool)
这是关于 data model 的文档:
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,__le__()
and__ge__()
are each other’s reflection, and__eq__()
and__ne__()
are their own reflection. If the operands are of different types, and right operand’s type is a direct or indirect subclass of the left operand’s type, the reflected method of the right operand has priority, otherwise the left operand’s method has priority. Virtual subclassing is not considered.
正如上面的评论所述,你想要的是什么,__eq__
本质上与潜在的 __req__
相同:它在 ==
的右侧调用如果左侧的对象 returns NotImplemented
:
In [1]: class A:
...: def __eq__(self, other):
...: return NotImplemented
...:
In [2]: class B:
...: def __eq__(self, other):
...: print("B comparing")
...: return True
...:
In [3]: B() == A()
B comparing
Out[3]: True
In [4]: A() == B()
B comparing
Out[4]: True
In [5]: A() == A()
Out[5]: False
它甚至可以与其他普通对象一起使用:
In [10]: 5 == B()
B comparing
Out[10]: True
但是,某些对象可能会在 __eq__
上产生 TypeError 而不是返回 NotImplemented
或 False
,这使得这对所有类型的对象都不可靠。
在您的案例中发生的情况是在您自己的 __eq__
方法中对数组和元组错误地使用了运算符 in
。 (感谢@wim 在这里的另一个答案中发现了这一点)。
__req__
在语言中不是一个好主意,因为如果 class Left
定义 __eq__
而 class Right
定义__req__
,那么 Python 必须就 Left() == Right()
中谁先被调用做出一致的决定。他们不可能都赢。
但是,Python 数据模型确实允许您在这里做您想做的事情。您可以从两侧控制此比较,但您需要正确定义 AnyOf
。 如果您希望AnyOf
从右侧控制__eq__,您必须将其定义为np.ndarray
的子class。
if I were to do
AnyOf(2,3) == arr
then myAnyOf
class's__eq__
method never gets called
不对,你这里有一个根本性的误解。左侧总是首先尝试相等比较,除非右侧是左侧类型的子class。
arr == AnyOf(2,3)
在上面的例子中,你的自定义 __eq__
被调用,因为 numpy 数组调用它!所以 np.ndarray
获胜,它决定每个元素检查一次。它实际上可以做任何其他事情,包括根本不调用您的 AnyOf.__eq__
。
AnyOf(2,3) == arr
在上面的例子中,你的 class 确实在比较中得到了第一次尝试,但由于你使用 in
的方式(检查数组是否在元组中)而失败了。