hash() 和 id() 的区别

Difference between hash() and id()

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

此外,

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

根据这个观察,我可以推断出以下几点吗?

哈希函数用于:

quickly compare dictionary keys during a dictionary lookup

ID函数用于:

Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.

Unequal objects may have the same hash values.

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

Equal objects need to have the same id values.

不,这通常是错误的。 @chepner 指出的一个简单反例是 5 == 5.0id(5) != id(5.0).

Whenever, obj1 is obj2 is called, the id values of both the objects is compared, not their hash values.

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

在试图理解 idhash 以及 ==is 运算符时,需要掌握三个概念:identity哈希值。并非所有对象都具有这三个。

  1. 所有对象都有一个 身份 ,尽管在某些情况下即使这样也可能有点棘手。 id 函数 return 是一个对应于对象标识的数字(在 cpython 中,它 return 是对象的内存地址,但其他解释器可能 return 其他内容)。如果两个对象(同时存在)具有相同的标识,则它们实际上是对同一对象的两个引用。 is 运算符按标识比较项目,a is b 等同于 id(a) == id(b).

    当您处理缓存在其实现中某处的对象时,标识可能会有点混乱。例如,cpython 中的小整数和字符串对象不会在每次使用时都重新创建。相反,现有对象会在需要时随时被 returned。但是你不应该在你的代码中依赖它,因为它是 cpython 的一个实现细节(其他解释器可能会以不同的方式或根本不这样做)。

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

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

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

    多个不同的对象可能具有相同的散列值,不过设计良好的散列函数会尽可能避免这种情况。在字典中存储具有相同散列的对象比存储具有不同散列的对象效率低得多(每次散列冲突都需要更多工作)。默认情况下,对象是可哈希的(因为它们的默认值是它们的身份,这是不可变的)。如果您在自定义 class 中编写 __eq__ 方法,Python 将禁用此默认哈希实现,因为您的 __eq__ 函数将为其实例定义值的新含义。如果您希望 class 仍然可哈希,您还需要编写一个 __hash__ 方法。如果您继承自可哈希 class 但不想自己被哈希,则可以在 class 正文中设置 __hash__ = None