【问题标题】:Slow equality evaluation for identical objects (x == x)相同对象的慢等式评估(x == x)
【发布时间】:2016-08-05 00:32:55
【问题描述】:

x == x 没有被快速评估有什么原因吗?我希望__eq__ 会检查它的两个参数是否相同,如果是,则立即返回 True。但它没有这样做:

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

对于内置,x == x 总是返回 True 我认为?对于用户定义的类,我想有人可以定义不满足此属性的__eq__,但是有任何合理的用例吗?

我希望快速评估x == x 的原因是因为当memoizing functions with very large arguments:

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

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

更新:

看来这个问题值得针对三种情况分别考虑。

1) 用户定义类型(即非内置/标准库)。

正如@donkopotamus 指出的那样,在某些情况下x == x 不应评估为True。例如,对于numpy.arraypandas.Series 类型,结果故意不能转换为布尔值,因为不清楚自然语义应该是什么(False 是否意味着容器为空,或者它是否意味着其中的所有项目都是 False? )。

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

def __eq__(self, other):
  if self is other:
    return True
  # continue normal evaluation

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

a) 非容器。

据我所知,这种情况下可能已经实现了短路 - 我无法确定,因为无论哪种方式都非常快。

b) 容器(包括str)。

正如@Karl Knechtel 评论的那样,如果在self is not other 的情况下,短路所节省的成本超过了额外开销,则添加短路可能会损害总体性能。虽然理论上是可能的,但即使在这种情况下,相对而言开销也很小(容器比较永远不会超快)。当然,在短路有帮助的情况下,可以节省大量资金。

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

【问题讨论】:

  • 'x == x' 对于内置函数并不总是 True - 特别是,NaN(非数字)永远不等于任何东西,甚至不等于它本身。
  • 我最好的猜测是“因为它会减慢所有其他比较,因此不会针对常见情况进行优化”。但是,当它是 O(1) 与 O(N) 的问题时,并且在某些情况下经常出现“罕见”案例是有实际原因的,我会支持你说应该做一个例外.不过,NaN 是该例外的一个例外……完全可以说它不应该是唯一的,并且其他用户定义的类可能有自己的原因。
  • x is y or x == y,简称x in (y,)
  • @Veedrac 我会,但不在lru_cache 中,也不在字典哈希表算法中..
  • @max 包装值以避免这种情况相当简单。

标签: python python-3.x python-internals


【解决方案1】:

正如你所说,有人可以很容易地定义一个你个人并不赞同的__eq__...例如,Institute of Electrical and Electronics Engineers 可能会很愚蠢地这样做:

>>> float("NaN") == float("NaN")
False

另一个“不合理的用例”:

>>> bool(numpy.ma.masked == numpy.ma.masked)
False

甚至:

>>> numpy.arange(10) == numpy.arange(10)
array([ True,  True,  True,  True,  True,  True,  True,  True,  True,  True], dtype=bool)

它甚至不能转换为bool

因此,x == x 肯定有实际范围自动短路为真。

偏离航线

不过,以下问题可能是个好问题:

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

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

>>> s = set([float("NaN")])
>>> s == s
True

嗯,这很有趣,尤其是因为:

>>> {float("NaN")} == {float("NaN")}
False

这种行为是由于 Python 对sequences to be reflexive 的渴望。

【讨论】:

  • +1 我不知道我是怎么忘记 NaN 的,我什至在我自己的 SO answer with the largest number of downvotes.../facepalm 中抱怨过这件事
  • 回答你提出的问题:[float('NaN')] == [float('NaN')]
  • @RyanHaining 有趣的是,我只是在写你的建议......你应该调查一下:L = [float('NaN')]; L == L[float('NaN')] == [float('NaN')]
  • @donkopotamus 现在我很困惑,也许它毕竟在检查is
  • 看起来像一个全新的问题
猜你喜欢
  • 2015-11-15
  • 2016-12-04
  • 1970-01-01
  • 1970-01-01
  • 2021-03-15
  • 2022-06-10
  • 1970-01-01
  • 2020-12-12
  • 1970-01-01
相关资源
最近更新 更多