hash()和id()之间的区别

时间:2015-12-21 18:57:15

标签: python hash equality

我有两个用户定义的对象,比如ab 这两个对象都具有相同的hash值 但是,id(a)id(b)不相等。

此外,

>>> a is b
False
>>> a == b
True

从这个观察中,我可以推断出以下情况吗?

  • 不等的对象可能具有相同的hash值。
  • 等于对象需要具有相同的id值。
  • 每当调用obj1 is obj2时,都会比较两个对象的id值,而不是hash值。

3 个答案:

答案 0 :(得分:29)

在尝试理解idhash以及==is运营商时,需要掌握三个概念:身份哈希值。并非所有对象都有三个。

  1. 所有对象都有标识,但即便如此,在某些情况下也会有点滑。 id函数返回对应于对象标识的数字(在cpython中,它返回对象的内存地址,但其他解释器可能返回其他内容)。如果两个对象(同时存在)具有相同的标识,则它们实际上是对同一对象的两个引用。 is运算符按标识对项目进行比较,a is b相当于id(a) == id(b)

    当您处理在其实现中某处缓存的对象时,身份可能会有点混乱。例如,cpython中的小整数和字符串的对象在每次使用时都不会重新生成。相反,现有对象会在需要时返回。您不应该在代码中依赖于此,因为它是cpython的实现细节(其他解释器可能以不同方式执行或根本不执行)。

  2. 所有对象也有,但这有点复杂。某些对象除了其标识之外没有其他有意义的值(因此,在某些情况下,标识可能是同义的)。值可以定义为==运算符比较的值,因此任何时候a == b,您都可以说ab具有相同的值。容器对象(如列表)具有由其内容定义的值,而其他一些对象将具有基于其属性的值。不同类型的对象有时可以具有相同的值,如数字:0 == 0.0 == 0j == decimal.Decimal("0") == fractions.Fraction(0) == False(是的,bool s是Python中的数字,出于历史原因)。

    如果某个类未定义__eq__方法(以实现==运算符),则它将继承object的默认版本,并且其实例将仅由其实例进行比较身份。当其他相同的实例可能具有重要的语义差异时,这是适当的。例如,连接到同一主机的同一端口的两个不同的套接字需要区别对待,如果一个是获取HTML网页而另一个是获取从该页面链接的图像,那么它们没有相同的值。 / p>

  3. 除了值之外,某些对象还有哈希值,这意味着它们可以用作词典键(并存储在set s中)。函数hash(a)返回对象a的哈希值,这是一个基于对象值的数字。对象的哈希值必须在对象的生命周期内保持不变,因此只有当一个对象的值是不可变的时(或者因为它基于对象的标识,或者因为它基于对象的内容),它才有意义。对象本身是不可变的。)

    多个不同的对象可能具有相同的哈希值,但设计良好的哈希函数将尽可能避免这种情况。在字典中存储具有相同散列的对象比存储具有不同散列的对象的效率低得多(每个散列冲突需要更多工作)。默认情况下,对象是可清除的(因为它们的默认值是它们的标识,它是不可变的)。如果在自定义类中编写__eq__方法,Python将禁用此默认哈希实现,因为您的__eq__函数将为其实例定义值的新含义。如果您希望您的课程仍然可以播放,您还需要编写__hash__方法。如果您从可散列的类继承但不希望自己可以使用,则可以在类主体中设置__hash__ = None

答案 1 :(得分:5)

  

不等的对象可能具有相同的哈希值。

是的,这是真的。一个简单的例子是CPython中的hash(-1) == hash(-2)

  

等于对象需要具有相同的id值。

一般来说,这不是假的。 @chepner注意到的一个简单反例是5 == 5.0id(5) != id(5.0)

  

每当调用obj1 is obj2时,都会比较两个对象的id值,而不是它们的哈希值。

是的,这是真的。 is比较对象的id是否相等(在CPython中它是对象的内存地址)。通常,这与对象的哈希值无关(对象甚至不需要可哈希)。

答案 2 :(得分:1)

哈希函数用于:

  

在字典查找期间快速比较字典键

ID功能用于:

  

返回对象的“标识”。这是一个整数,在该生命周期内保证该对象是唯一且恒定的。具有非重叠生存期的两个对象可能具有相同的id()值。