我有一个小帮手班:
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__
等方法。)
3条答案
按热度按时间z4iuyo4d1#
在语言中,
__req__
可能被认为比“有用”更令人困惑,考虑一下,如果类Left
定义__eq__
,类Right
定义__req__
,那么Python就必须做出一个“一致”的决定,决定在Left() == Right()
中谁先被调用(我们大概希望结果是等价的,无论哪种方式),他们不可能都赢。然而,Python数据模型允许你在这里做你想做的事情。比较可以从操作的任何一端控制,但是你需要以一种特殊的方式定义
AnyOf
。当比较的左边是一个np.ndarray
示例时,为了从右边控制eq**,AnyOf
应该是np.ndarray
的子类。**如果我执行
AnyOf(2,3) == arr
,那么我的AnyOf
类的__eq__
方法永远不会被调用实际上,不,这里有一个明显的基本误解,左边的类型 * 总是 * 首先尝试处理相等比较,除非右边的类型是左边类型的子类。
在上面的比较中,你的定制
__eq__
* 被调用了,因为numpy数组调用了它!所以np.ndarray
胜出,它决定对每个元素检查一次,它可以做任何其他事情,包括根本不调用你的AnyOf.__eq__
。在上面显示的比较中,您的类确实获得了比较的第一次尝试,但由于
in
的使用方式-return other in self.elements
正在检查数组是否在元组中,所以它失败了。xytpbqjk2#
以下是有关数据模型的文档:
这些方法没有参数交换的版本(当左边的参数不支持操作而右边的参数支持时使用);相反,
__lt__()
和__gt__()
是彼此的反射,__le__()
和__ge__()
是彼此的反射,并且**__eq__()
**和__ne__()
是它们自己的反射。如果操作数是不同类型的,并且右操作数的类型是左操作数的类型的直接或间接子类,则右操作数的反射方法具有优先级。否则左操作数的方法优先,不考虑虚子类。正如上面的注解所述,您所需要的是可行的,
__eq__
本质上与潜在的__req__
相同:如果左手的对象返回NotImplemented
,则在==
的右侧调用该函数:当它出现时,它甚至可以与其他普通对象一起工作:
但是,某些对象可能会在
__eq__
上生成TypeError,而不是返回NotImplemented
或False
,这使得这对于所有类型的对象都不可靠。在您的例子中,发生的是在您自己的
__eq__
方法中对数组和元组错误地使用了操作符in
。(感谢@wim在这里的另一个答案中发现了这一点)。siotufzp3#
关于
__rxx__
方法(如__radd__
)的文档指出:仅当左操作数不支持相应的运算并且操作数属于不同类型时,才调用这些函数。
虽然类默认情况下没有
__add__
或__sub__
方法,但它们确实有__eq__
:这意味着
__req__
永远不会被调用,除非您显式地从其他类中删除__eq__
或使__eq__
返回NotImplemented
。您可以使用
np.in1d
解决您的特定问题: