对相同对象的慢等式评估(x == x)

max*_*max 15 python python-3.x python-internals

有没有什么理由x == x不能快速评估?我希望__eq__能检查它的两个参数是否相同,如果是,则立即返回True.但它没有这样做:

s = set(range(100000000))
s == s # this doesn't short-circuit, so takes ~1 sec
Run Code Online (Sandbox Code Playgroud)

对于内置插件,x == x总是返回True我认为?对于用户定义的类,我想有人可能会定义__eq__不满足此属性,但是有没有合理的用例呢?

我想之所以x == x能够快速评估是因为它是在一个巨大的性能损失非常大的参数memoizing功能:

from functools import lru_cache
@lru_cache()
def f(s):
    return sum(s)
large_obj = frozenset(range(50000000))
f(large_obj) # this takes >1 sec every time
Run Code Online (Sandbox Code Playgroud)

请注意,@ lru_cache 对大型对象反复慢的原因并不是因为它需要计算__hash__(这只是执行一次然后由@jsbueno 指出的硬缓存),但因为__eq__ 每次都需要执行字典的哈希表确保它在桶中找到正确的对象(哈希的相等性显然是不够的).

更新:

对于三种情况,似乎值得分别考虑这个问题.

1)用户定义的类型(即,不是内置/标准库).

正如@donkopotamus指出的那样,有些情况下x == x不应该评估为True.例如,for numpy.arraypandas.Seriestypes,结果有意无法转换为boolean,因为它不清楚自然语义应该是什么(False意味着容器是空的,还是意味着它中的所有项都是False?).

但是在这里,python不需要做任何事情,因为x == x如果合适的话,用户总是可以自己进行短路比较:

def __eq__(self, other):
  if self is other:
    return True
  # continue normal evaluation
Run Code Online (Sandbox Code Playgroud)

2)Python内置/标准库类型.

a)非容器.

据我所知,在这种情况下可能已经实现了短路 - 我无法分辨,因为它的速度超快.

b)容器(包括str).

正如@Karl Knechtel评论的那样,如果短路的节省被额外开销所抵消,那么增加短路可能会损害总体性能self is not other.虽然理论上可行,但即使在这种情况下,相对而言开销也很小(容器比较永远不会超快).当然,在短路有帮助的情况下,节省的费用可能会非常大.

顺便说一句,事实证明它str确实是短路:比较巨大的相同字符串是即时的.

don*_*mus 5

正如你所说,有人可以很容易地定义一个__eq__你个人不会赞同的......例如,电气和电子工程师协会可能是如此愚蠢:

>>> float("NaN") == float("NaN")
False
Run Code Online (Sandbox Code Playgroud)

另一个"不合理的用例":

>>> bool(numpy.ma.masked == numpy.ma.masked)
False
Run Code Online (Sandbox Code Playgroud)

甚至:

>>> numpy.arange(10) == numpy.arange(10)
array([ True,  True,  True,  True,  True,  True,  True,  True,  True,  True], dtype=bool)
Run Code Online (Sandbox Code Playgroud)

有大胆甚至不能转换为bool!

因此x == x,不能自动短路是真实存在的实际范围.

走了课程

但是,以下可能是一个很好的问题:

为什么不set.__eq__检查实例身份?

好吧,有人可能会认为......因为一个集合S可能包含NaN并且因为NaN它不能等于自己,那么这样的集合肯定S不能自相等?调查:

>>> s = set([float("NaN")])
>>> s == s
True
Run Code Online (Sandbox Code Playgroud)

嗯,这很有意思,尤其是:

>>> {float("NaN")} == {float("NaN")}
False
Run Code Online (Sandbox Code Playgroud)

这种行为是由于Python希望序列具有反身性.

  • @RyanHaining有趣的是,我只是在写你的建议......你应该调查:`L = [float('NaN')]; L == L`与`[float('NaN')] == [float('NaN')]` (3认同)