为什么Python没有"__req__"(反映相等)方法?

acd*_*cdr 6 python numpy python-datamodel python-internals

我有一个小助手班:

class AnyOf(object):
    def __init__(self, *args):
        self.elements = args
    def __eq__(self, other):
        return other in self.elements
Run Code Online (Sandbox Code Playgroud)

这让我做了甜蜜的魔术:

>>> arr = np.array([1,2,3,4,5])
>>> arr == AnyOf(2,3)
np.array([False, True, True, False, False])
Run Code Online (Sandbox Code Playgroud)

无需使用列表理解(如np.array(x in (2,3) for x in arr).

(我维护一个让(可信)用户输入任意代码的用户界面,并且a == AnyOf(1,2,3)比非技术精明用户的列表理解更加可口.)

然而!

这只适用于一种方式!例如,如果我这样做,AnyOf(2,3) == arr那么我的AnyOf类的__eq__方法永远不会被调用:相反,NumPy数组的__eq__方法被调用,在内部(我会假设)调用其__eq__所有元素的方法.

这让我想知道:为什么Python不允许右侧等效__eq__?(大致相当于像__radd__,__rmul__等等的方法.)

wim*_*wim 6

一个__req__是不是在语言是一个好主意,因为如果类Left定义__eq__和类Right定义__req__,那么Python有义务作出关于谁最先被叫做一致的决定Left() == Right().他们不可能双赢.

但是,Python数据模型确实允许您在此处执行所需操作.从两侧您可以控制此比较,但您需要AnyOf正确定义. 如果AnyOf要从右侧控制__eq__,则必须将其定义为的子类np.ndarray.

如果我这样做,AnyOf(2,3) == arr那么我的AnyOf班级__eq__方法永远不会被调用

不,你在这里有一个根本的误解.除非右侧是左侧类型的子类,否则左侧总是首先尝试相等比较.

arr == AnyOf(2,3)
Run Code Online (Sandbox Code Playgroud)

在上述情况下,您的自定义__eq__ 被调用,因为numpy的数组调用它!所以np.ndarray赢了,并决定每个元素检查一次.它确实可以做任何其他事情,包括根本不打电话给你AnyOf.__eq__.

AnyOf(2,3) == arr
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,你的类确实在比较时首次尝试,并且由于你使用的方式in(检查数组是否在元组中)而失败.

  • 关于“在语言中`__req__` 不是一个好主意”以及随后的论点,我认为这不成立,因为从同一个论点来看,像`__radd__` 这样的方法也不是一个好主意。但是,我现在确实看到,由于选择哪个模型(`Left.__eq__` 或`Right.__req__`),它实际上会使得`__req__` 实现与`__eq__` 完全相同的功能,使得右手方法多余! (3认同)

jsb*_*eno 5

这是有关数据模型的文档:

\n\n
\n

这些方法没有交换参数版本(当左参数不支持该操作但右参数支持时使用);相反,__lt__()__gt__()是彼此\xe2\x80\x99s\n 的反射,__le__()__ge__()是彼此\xe2\x80\x99s 的反射,并且\n __eq__()__ne__()是它们自己的反射。如果操作数的类型不同,并且右操作数\xe2\x80\x99s类型是左操作数\xe2\x80\x99s类型的直接或间接子类,则右操作数的反射方法具有优先级,否则左操作数\xe2\x80\x99s 方法具有优先级。不考虑虚拟子类化。

\n
\n\n

正如上面的评论中所述,您想要的有效,并且__eq__本质上与潜在的相同:如果左侧的对象返回,则在右侧__req__调用它:==NotImplemented

\n\n
In [1]: class A:\n   ...:     def __eq__(self, other):\n   ...:         return NotImplemented\n   ...:     \n\nIn [2]: class B:\n   ...:     def __eq__(self, other): \n   ...:         print("B comparing")\n   ...:         return True\n   ...:     \n\nIn [3]: B() == A()\nB comparing\nOut[3]: True\n\nIn [4]: A() == B()\nB comparing\nOut[4]: True\n\nIn [5]: A() == A()\nOut[5]: False\n
Run Code Online (Sandbox Code Playgroud)\n\n

它甚至可以与其他普通对象一起使用:

\n\n
In [10]: 5 == B()\nB comparing\nOut[10]: True\n
Run Code Online (Sandbox Code Playgroud)\n\n

但是,某些对象可能会产生 TypeError on__eq__而不是返回NotImplementedor False,这使得这对于所有类型的对象都不可靠。

\n\n

在您的情况下发生的情况是in 在您自己的__eq__方法中错误地使用了带有数组和元组的运算符。(感谢@wim 在另一个答案中发现了这一点)。

\n