Skip to content

Latest commit

 

History

History
391 lines (285 loc) · 11.3 KB

Python_Hashes_and_Equality.md

File metadata and controls

391 lines (285 loc) · 11.3 KB

python 字典中键的问题

>>> {True: 'yes', 1: 'no', 1.0: 'maybe'}
{True: 'maybe'}

>>> True == 1 == 1.0
True
>>> hash(True), hash(1), hash(1.0)
(1, 1, 1)
>>>

对象相等,哈希值不同

class OnlyEquals:
    def __eq__(self, other):
        return True

    def __hash__(self):
        return id(self)

print OnlyEquals() == OnlyEquals()  # True
print OnlyEquals() == 42  # True
print OnlyEquals() == 'wait'  # True

objects = [OnlyEquals(), OnlyEquals(), OnlyEquals()]
print [hash(o) for o in objects]
# [140525112345520, 140525112345448, 140525112345592]

print {OnlyEquals(): 1, OnlyEquals(): 2}
"""
{
<__main__.OnlyEquals instance at 0x7fee3ab12440>: 1, 
<__main__.OnlyEquals instance at 0x7fee3ab12488>: 2
}
"""

对象不相等,哈希值相同

class OnlySomeHash:
    def __eq__(self, other):
        return False

    def __hash__(self):
        return 1

print OnlySomeHash() == OnlySomeHash()  # False
print OnlySomeHash() == 42  # False
print OnlySomeHash() == 'wait'  # False

objects = [OnlySomeHash(), OnlySomeHash(), OnlySomeHash()]
print [hash(o) for o in objects]
# [1, 1, 1]

print {OnlySomeHash(): 1, OnlySomeHash(): 2}
"""
{
<__main__.OnlySomeHash instance at 0x7f34a7979440>: 1, 
<__main__.OnlySomeHash instance at 0x7f34a7979488>: 2
}
"""

对象相等,哈希值相同

class EqualsAndSomeHash:
    def __eq__(self, other):
        return True

    def __hash__(self):
        return 1

print EqualsAndSomeHash() == EqualsAndSomeHash()  # True
print EqualsAndSomeHash() == 42  # True
print EqualsAndSomeHash() == 'wait'  # True

objects = [EqualsAndSomeHash(), EqualsAndSomeHash(), EqualsAndSomeHash()]
print [hash(o) for o in objects]
# [1, 1, 1]

print {EqualsAndSomeHash(): 1, EqualsAndSomeHash(): 2}
"""
{<__main__.EqualsAndSomeHash instance at 0x7f4e215ab440>: 2}
"""

Python Hashes and Equality

对象可hash的方法

一个可散列的对象必须满足以下要求:

  1. 支持 hash() 函数,并且通过 __hash__() 方法所得到的散列值是不变的。
  2. 支持通过 __eq__() 方法来检测相等性。
  3. 若 a == b 为真, 则 hash(a) == hash(b) 也为真。

Notes:

  1. 所有由用户自定义的对象默认都是可散列的, 因为它们的散列值由id() 来获取, 而且它们都是不相等的。
  2. 如果你实现了一个类的 __eq__ 方法, 并且希望它是可散列的, 那么它一定要有个恰当的 __hash__ 方法, 保证在 a == b 为真的情况下 hash(a) == hash(b) 也必定为真。
实现方式 是否可 hash 原因
__hash__(self) 和 __eq__(self, other) 都不实现 可 hash 所有由用户自定义的对象默认都是可散列的, 因为它们的散列值由id() 来获取, 而且它们都是不相等的
实现 __hash__(self),不实现__eq__(self, other) 可 hash 支持 hash() 函数,并且通过 __hash__() 方法所得到的散列值是不变的;所有由用户自定义的对象默认都是不相等的
__hash__(self) 和 __eq__(self, other) 都实现 可 hash 支持 hash() 函数,并且通过 __hash__() 方法所得到的散列值是不变的; 支持通过 __eq__() 方法来检测相等性;如果你实现了一个类的 __eq__ 方法, 并且希望它是可散列的, 那么它一定要有个恰当的 __hash__ 方法, 保证在 a == b 为真的情况下 hash(a) == hash(b) 也必定为真
不实现__hash__(self), 实现__eq__(self, other) 不可 hash 如果你实现了一个类的 __eq__ 方法, 并且希望它是可散列的, 那么它一定要有个恰当的 __hash__ 方法, 保证在 a == b 为真的情况下 hash(a) == hash(b) 也必定为真
  • __hash__(self) 和 __eq__(self, other) 都不实现
    • 所有由用户自定义的对象默认都是可散列的, 因为它们的散列值由id() 来获取, 而且它们都是不相等的
  • 实现 __hash__(self),不实现 __eq__(self, other)
    • 支持 hash() 函数,并且通过 __hash__() 方法所得到的散列值是不变的
    • 所有由用户自定义的对象默认都是不相等的
  • __hash__(self) 和 __eq__(self, other) 都实现
    • 支持 hash() 函数,并且通过 __hash__() 方法所得到的散列值是不变的
    • 支持通过 __eq__() 方法来检测相等性
    • 如果你实现了一个类的 __eq__ 方法, 并且希望它是可散列的, 那么它一定要有个恰当的 __hash__ 方法, 保证在 a == b 为真的情况下 hash(a) == hash(b) 也必定为真

如果只实现 __eq__(self, other),不实现 __hash__(self),则对象不可hash

#!/usr/bin/env python
# -*- coding: utf-8 -*-

class C1:
    ''' __hash__(self) 和  __eq__(self, other) 都不实现
    所有由用户自定义的对象默认都是可散列的, 因为它们的散列值由id() 来获取, 而且它们都是不相等的
    '''
    def __init__(self, x):
        self.x = x

print hash(C1(1))  # 8736312111280

class C2:
    '''实现 __hash__(self),不实现 __eq__(self, other)
    支持 hash() 函数,并且通过 __hash__() 方法所得到的散列值是不变的
    所有由用户自定义的对象默认都是不相等的
    '''
    def __init__(self, x):
        self.x = x

    def __hash__(self):
        return hash(self.x)

print hash(C2(1))  # 1

class C3:
    ''' __hash__(self) 和  __eq__(self, other) 都实现
    支持 hash() 函数,并且通过 __hash__() 方法所得到的散列值是不变的
    支持通过 __eq__() 方法来检测相等性
    如果你实现了一个类的 __eq__ 方法, 并且希望它是可散列的, 那么它一定要有个恰当的 __hash__ 方法, 保证在 a == b 为真的情况下 hash(a) == hash(b) 也必定为真
    '''
    def __init__(self, x):
        self.x = x

    def __hash__(self):
        return hash(self.x)

    def __eq__(self, other):
        return (
            self.__class__ == other.__class__ and
            self.x == other.x
        )

print hash(C3(1))  # 1

class C4:
    '''只实现 __eq__(self, other),不实现 __hash__(self)
    '''
    def __init__(self, x):
        self.x = x

    def __eq__(self, other):
        return (
            self.__class__ == other.__class__ and
            self.x == other.x
        )

print hash(C4(1))  # TypeError: unhashable instance

对象是否可以作为set或者dictionary的key

对象作为key值则对象必须可hash

同样的参数是否可以作为同样的key

hash equality 是否可以作为同样的key
相同 相等 同一个key
相同 不相等 不同的key
不相同 相等 不可 hash, 不能作为key
不相同 不相等 不同的key
#!/usr/bin/env python
# -*- coding: utf-8 -*-

class C1:
    ''' __hash__(self) 和  __eq__(self, other) 都不实现
    所有由用户自定义的对象默认都是可散列的, 因为它们的散列值由id() 来获取, 而且它们都是不相等的
    '''
    def __init__(self, x):
        self.x = x

# 测试是否可hash
print hash(C1(1))  # 8728643443659

# 对象是否可以作为set或者dictionary的key
obj1 = C1(1)
obj2 = C1(1)
print hash(obj1), hash(obj2)  # 8728643443659 -9223363308211332145
print obj1 == obj2  # False
s = set()
s.add(obj1)
s.add(obj2)
print len(s)  # 2
print '************' * 10


class C2:
    '''实现 __hash__(self),不实现 __eq__(self, other)
    支持 hash() 函数,并且通过 __hash__() 方法所得到的散列值是不变的
    所有由用户自定义的对象默认都是不相等的
    '''
    def __init__(self, x):
        self.x = x

    def __hash__(self):
        return hash(self.x)

# 测试是否可hash
print hash(C2(1))  # 1

# 对象是否可以作为set或者dictionary的key
obj1 = C2(1)
obj2 = C2(1)
print hash(obj1), hash(obj2)  # 1 1
print obj1 == obj2  # False
s = set()
s.add(obj1)
s.add(obj2)
print len(s)  # 2
print '************' * 10


class C3:
    ''' __hash__(self) 和  __eq__(self, other) 都实现
    支持 hash() 函数,并且通过 __hash__() 方法所得到的散列值是不变的
    支持通过 __eq__() 方法来检测相等性
    如果你实现了一个类的 __eq__ 方法, 并且希望它是可散列的, 那么它一定要有个恰当的 __hash__ 方法, 保证在 a == b 为真的情况下 hash(a) == hash(b) 也必定为真
    '''
    def __init__(self, x):
        self.x = x

    def __hash__(self):
        return hash(self.x)

    def __eq__(self, other):
        return (
            self.__class__ == other.__class__ and
            self.x == other.x
        )

# 测试是否可hash
print hash(C3(1))  # 1

# 对象是否可以作为set或者dictionary的key
obj1 = C3(1)
obj2 = C3(1)
print hash(obj1), hash(obj2)  # 1 1
print obj1 == obj2  # True
s = set()
s.add(obj1)
s.add(obj2)
print len(s)  # 1
print '************' * 10


class C4:
    '''只实现 __eq__(self, other),不实现 __hash__(self)
    '''
    def __init__(self, x):
        self.x = x

    def __eq__(self, other):
        return (
            self.__class__ == other.__class__ and
            self.x == other.x
        )
# 测试是否可hash
# print hash(C4(1))  # TypeError: unhashable instance

# 对象是否可以作为set或者dictionary的key
obj1 = C4(1)
obj2 = C4(1)
# print hash(obj1), hash(obj2)  # TypeError: unhashable instance
print obj1 == obj2  # True
s = set()
# s.add(obj1)  # TypeError: unhashable instance
# s.add(obj2)  # TypeError: unhashable instance
print len(s)  # 0
print '************' * 10

对象的hash值在生命周期内发生变化

#!/usr/bin/env python
# -*- coding: utf-8 -*-

class C:
    def __init__(self, x):
        self.x = x

    def __hash__(self):
        return hash(self.x)

    def __eq__(self, other):
        return (self.__class__ == other.__class__ and self.x == other.x)

d = dict()
s = set()

c1 = C(1)
print hash(c1)  # 1

# 对象c1可hash,可以作为set和dictionary的key值
d[c1] = 42
s.add(c1)

print c1 in s  # True
print c1 in d  # True

c1.x = 2
print hash(c1)  # 2

# 对象c1的hash值在生命周期内发生变化,变化后将不能作为set和dictionary的key值
print c1 in s  # False
print c1 in d  # False

结论

  • An object is hashable if it has a hash value which never changes during its lifetime (it needs a __hash__() method), and can be compared to other objects (it needs an __eq__() or __cmp__() method).
  • Hashable objects which compare equal must have the same hash value.
  • Hashability makes an object usable as a dictionary key and a set member, because these data structures use the hash value internally.
  • All of Python’s immutable built-in objects are hashable, while no mutable containers (such as lists or dictionaries) are.
  • Objects which are instances of user-defined classes are hashable by default; they all compare unequal, and their hash value is their id().
  • If two objects are equal, then their hashes should be equal.
  • If two objects have the same hash, then they are likely to be the same object.

参考