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.array和pandas.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确实是短路:比较巨大的相同字符串是即时的.
正如你所说,有人可以很容易地定义一个__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希望序列具有反身性.