Python hash()和id()之间的差异

Python hash()和id()之间的差异,python,hash,equality,Python,Hash,Equality,我有两个用户定义的对象,比如a和b 这两个对象具有相同的哈希值。 但是,id(a)和id(b)是不相等的 而且 >>> a is b False >>> a == b True 根据这一观察,我能推断出以下几点吗 不相等的对象可能具有相同的哈希值 相等的对象需要具有相同的id值 每当调用obj1为obj2时,都会比较两个对象的id值,而不是它们的hash值 哈希函数用于: 在字典查找过程中快速比较字典键 ID函数用于: 返回对象的“标识”。这是一个整数,保

我有两个用户定义的对象,比如
a
b

这两个对象具有相同的
哈希值。
但是,
id(a)
id(b)
是不相等的

而且

>>> a is b
False
>>> a == b
True
根据这一观察,我能推断出以下几点吗

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

哈希函数用于:

在字典查找过程中快速比较字典键

ID函数用于:

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

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

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

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

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

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


是的,这是真的
is
比较对象的
id
是否相等(在CPython中,它是对象的内存地址)。一般来说,这与对象的散列值无关(对象甚至不需要是可散列的)。

在试图理解
id
hash
=
is
运算符时,有三个概念需要掌握:标识散列值。并非所有对象都具有这三个属性

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

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

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

    如果一个类没有定义
    \uuuu eq\uuu
    方法(实现
    =
    操作符),它将从
    对象继承默认版本,其实例将仅通过它们的标识进行比较。当相同的实例可能有重要的语义差异时,这是合适的。例如,如果一个正在获取HTML网页,另一个正在获取从该网页链接的图像,则需要对连接到同一主机的同一端口的两个不同套接字进行不同的处理,因此它们的值不同

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

    多个不同的对象可能具有相同的哈希值,尽管设计良好的哈希函数将尽可能避免这种情况。在字典中存储具有相同哈希的对象要比存储具有不同哈希的对象效率低得多(每次哈希冲突都需要更多的工作)。对象在默认情况下是可散列的(因为它们的默认值是它们的标识,标识是不可变的)。如果在自定义类中编写一个
    \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    方法,Python将禁用此默认哈希实现,因为您的
    \uuuuuuu。如果希望类仍然是可散列的,那么还需要编写一个
    \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    方法。如果您从可散列类继承,但不想自己成为可散列类,则可以在类主体中设置
    \uuuuuuuuuuuuuuuuuuuuuuu=None


  • 第二个断言的反例:
    5==5.0
    vs
    id(5)==id(5.0)
    @ajcr-Even
    []是[]
    返回
    False
    ,即使
    id([])==id([])
    返回
    True<