散列生成器表达式

tay*_*ift 5 python hash generator

显然python将允许我哈希生成器表达式,如 (i for i in [1, 2, 3, 4, 5])

>>> hash(i for i in [1, 2, 3, 4, 5])
8735741846615
Run Code Online (Sandbox Code Playgroud)

然而,仔细观察,无论我投入哪个生成器,这个哈希值总是相同的!

>>> hash(i for i in range(2))
8735741846615
>>> hash(i for i in [1, 2, 3, 4, 5, 6])
8735741846615
>>> hash(i for i in [0, 1, 2, 3, 4, 5, 6])
8735741846615
Run Code Online (Sandbox Code Playgroud)

为什么会这样?为什么甚至允许对发生器进行散列?

我需要这样做,因为我将东西存储在字典缓存中.您可以通过传递对象列表来访问缓存中的条目.但是,具有不同项目的某些列表组仍应指向相同的条目,因为此处唯一重要的是每个列表项中一个属性的整数值.因此,它们应仅基于这些整数值进行散列,而不是与项本身相关的任何内容,以避免不必要的缓存未命中.

我知道你总是可以将表达式转换为元组,但我问你是否可以通过简单地使用没有元组容器的生成器的输出来绕过它,类似于如何sum()用于这样的事情.

Mis*_*agi 5

所以这里有两个问题:

  1. 为什么生成器有哈希值,例如列表没有?
  2. 为什么你总是得到相同的哈希?

对于2,答案很简单:在这种情况下,哈希基于对象的id.由于您实际上并未存储该对象,因此其内存将被重用.这意味着下一个生成器具有相同的id哈希值.

对于1,答案是"因为他们可以".hash主要是指用于在使用中dict,set与其他情况下它允许识别一个对象.这些情况设定了a == b也暗示的约束hash(a) == hash(b)(反过来不受约束).

现在,对list,dict和其他收藏品,平等的基础上的内容.[1,2,3] == [1,2,3]例如,无论两者是否都是相同的对象.这意味着如果向他们添加某些东西,他们的平等会发生变化,因此他们hash也会改变.因此,hash未定义,因为它必须是一个常数,它在其他工作dict.

相反,发电机可以具有任何内容.考虑例如提供随机值的生成器.因此,按内容比较生成器是没有意义的.他们只是通过身份进行比较.所以,a == b等于id(a) == id(b)发电机.反过来,这意味着基础hash(a)id(a)通过平等永远满足约束hash.