numpy 为什么Python没有“__req__”(反射等式)方法?

dvtswwa3  于 2023-03-12  发布在  Python
关注(0)|答案(3)|浏览(91)

我有一个小帮手班:

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类的__eq__方法永远不会被调用:而是调用NumPy数组的__eq__方法,该方法在内部(我假设)调用其所有元素的__eq__方法。
这使我不禁要问:为什么Python不允许__eq__的右侧等价项?(大致等价于__radd____rmul__等方法。)

z4iuyo4d

z4iuyo4d1#

在语言中,__req__可能被认为比“有用”更令人困惑,考虑一下,如果类Left定义__eq__,类Right定义__req__,那么Python就必须做出一个“一致”的决定,决定在Left() == Right()中谁先被调用(我们大概希望结果是等价的,无论哪种方式),他们不可能都赢。
然而,Python数据模型允许你在这里做你想做的事情。比较可以从操作的任何一端控制,但是你需要以一种特殊的方式定义AnyOf当比较的左边是一个np.ndarray示例时,为了从右边控制eq**,AnyOf应该是np.ndarray的子类。**
如果我执行AnyOf(2,3) == arr,那么我的AnyOf类的__eq__方法永远不会被调用
实际上,不,这里有一个明显的基本误解,左边的类型 * 总是 * 首先尝试处理相等比较,除非右边的类型是左边类型的子类。

arr == AnyOf(2,3)

在上面的比较中,你的定制__eq__ * 被调用了,因为numpy数组调用了它!所以np.ndarray胜出,它决定对每个元素检查一次,它可以做任何其他事情,包括根本不调用你的AnyOf.__eq__

AnyOf(2,3) == arr

在上面显示的比较中,您的类确实获得了比较的第一次尝试,但由于in的使用方式-return other in self.elements正在检查数组是否在元组中,所以它失败了。

xytpbqjk

xytpbqjk2#

以下是有关数据模型的文档:
这些方法没有参数交换的版本(当左边的参数不支持操作而右边的参数支持时使用);相反,__lt__()__gt__()是彼此的反射,__le__()__ge__()是彼此的反射,并且**__eq__()**和__ne__()是它们自己的反射。如果操作数是不同类型的,并且右操作数的类型是左操作数的类型的直接或间接子类,则右操作数的反射方法具有优先级。否则左操作数的方法优先,不考虑虚子类。
正如上面的注解所述,您所需要的是可行的,__eq__本质上与潜在的__req__相同:如果左手的对象返回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,而不是返回NotImplementedFalse,这使得这对于所有类型的对象都不可靠。
在您的例子中,发生的是在您自己的__eq__方法中对数组和元组错误地使用了操作符in。(感谢@wim在这里的另一个答案中发现了这一点)。

siotufzp

siotufzp3#

关于__rxx__方法(如__radd__)的文档指出:
仅当左操作数不支持相应的运算并且操作数属于不同类型时,才调用这些函数。
虽然类默认情况下没有__add____sub__方法,但它们确实__eq__

>>> class A(object):
...     pass
>>> '__eq__' in dir(A)
True

这意味着__req__永远不会被调用,除非您显式地从其他类中删除__eq__或使__eq__返回NotImplemented
您可以使用np.in1d解决您的特定问题:

>>> np.in1d(arr, [2, 3])
array([False,  True,  True, False, False], dtype=bool)

相关问题