首先我们了解下哈希以及哈希值的概念,哈希的用途,下面是摘自某网站对hashing的概念介绍:
Hashing is the process of using an algorithm to map data of any size to a fixed length. This is called a hash value. Hashing is used to create high performance, direct access data structures where large amount of data is to be stored and accessed quickly. Hash values are computed with hash functions.
意思是:哈希是一种算法,可以将任意大小的数据映射成固定的长度,这个值就叫哈希值。哈希提高了执行效率,可以直接访问那些存储大量数据的数据结构,而且访问速度很快。哈希值可以通过哈希函数计算出来。
然后我们了解可哈希和不可哈希(hashable,unhashable)
An object is hashable if it has a hash value which never changes during its lifetime. (It can have different values during multiple invocations of Python programs.) A hashable object needs a hash() method. In order to perform comparisons, a hashable needs an eq() method.
意思是:如果一个对象在其生命周期内(没有回收对象情况下)存在一个从来不会改变的哈希值,那么这个对象是可哈希的(如果多次调用python程序期间,会有不同的哈希值)。一个可哈希的对象肯定有一个 __hash__()方法,如果为了便于比较对象哈希值是否相同,可哈希的对象还需要__eq__() 方法。而且一个相同的哈希对象,他们一定有相同的哈希值。
python内置数据结构中不可变的对象是可哈希的,可变容器例如字典、列表是可变的,也是不可哈希的;用户自定义的类对象实例默认是可哈希的。
归结为一句话,可变不可哈希,不可变可哈希。
整型integer、字符串string、元组tuple,都是immutable不可变的,因此是可哈希的。
11val = 100
22print(val.__hash__()) #整型 integer print 100
33print("python".__hash__()) #字符串 string print 1494747378
44print((1,).__hash__()) # 元组 tuple print 1956216267
python自定义对象默认是是可哈希的,他们的哈希值源自他们的id(),即与内存地址有关。
1 1#自定义类
2 2class User:
3 3 def __init__(self,name, occupation):
4 4 self.name = name
5 5 self.occupation = occupation
6 6u1 = User("小王","司机")
7 7u2 = User("小王","司机")
8 8print("u1的hash值",hash(u1)) # 每次执行哈希值都在变
9 9print("u2的hash值",hash(u2)) #
1010if u1 == u2:
1111 print("the same user")
1212else:
1313 print("different users")
1414##两个相同的实例,哈希值却不同,为什么?
虽然两个实例细节参数相同,但上面输出different users,说明两个实例并不相同。为了解决这一问题,我们可以重新定义__eq__方法:
1 1class User:
2 2 def __init__(self,name, occupation):
3 3 self.name = name
4 4 self.occupation = occupation
5 5 def __eq__(self, other):
6 6 return self.name == other.name and self.occupation == other.occupation
7 7 def __str__(self):
8 8 return f'{self.name} {self.occupation}'
9 9u1 = User("小王","司机")
1010u2 = User("小王","司机")
1111#print("u1的hash值",hash(u1)) #重写了__eq__方法,没有重写__hash__方法,则不可哈希
1212#print("u2的hash值",hash(u2))
1313#print(id(u1),id(u2))
1414if (u1 == u2):
1515 print("the same user")
1616else:
1717 print("different users") #print
1818print("u1",u1) #调用__str__方法
1919# print({u1,u2}) # 定义了__eq__方法,没有定义__hash__方法,则实例不可用于可哈希集中的项
上面输出the same user,说明两个实例比较相同,但又出现了新问题,没有eq方法时调用hash()方法没问题,有eq方法时调用hash()方法会报错TypeError: unhashable type: 'User',所以需要重新定义__hash__方法:
1 1class User:
2 2 def __init__(self,name, occupation):
3 3 self.name = name
4 4 self.occupation = occupation
5 5 def __eq__(self, other):
6 6 return self.name == other.name and self.occupation == other.occupation
7 7 def __hash__(self):
8 8 return hash((self.name,self.occupation)) #str(self.name)+str(self.occupation)
9 9 def __str__(self):
1010 return f'{self.name} {self.occupation}'
1111u1 = User("小王","司机")
1212u2 = User("小王","司机")
1313print("u1的hash值",hash(u1)) #此时两个实例的哈希值相同。
1414print("u2的hash值",hash(u2))
1515#print(id(u1),id(u2))
1616if (u1 == u2):
1717 print("the same user")
1818else:
1919 print("different users") #print
2020print("u1",u1) #调用__str__方法
2121print({u1,u2}) #没有报错
上面的__hash__类方法返回值为hash((self.name,self.occupation)),用两个初始化实例属性组成的元组的哈希值,但仔细发现设置成hash(str(self.name)+str(self.occupation)),用两个初始化实例属性字符串相加组成的新字符串的哈希值好像也是可以。而且hash(u1)也没有此时也不再报错。并且,可以将实例添加到集合set中,打印集合{u1,u2}也不再报错。改变属性,我们可以输出想要的输出内容。
hash()函数返回的对象的哈希值,通过调用对象的 hash() 函数,哈希值返回值可以是整型、字符串、浮点类型,哈希值用于在字典查找中快速比较字典键。
11print("hash value of 181",hash(181)) #181
22print("Hash for 181.23 is:",hash(181.23)) # 579773580
33print("Hash for Python is:",hash("Python")) #-113230000
下面是其他的重写hash类方法的例子:
1 1class Emp:
2 2 def __init__(self, aId_in, aName_in):
3 3 self.myId = aId_in
4 4 self.myName = aName_in
5 5 def __hash__(self):
6 6 h = hash("{}.{}".format(self.myId, self.myName))
7 7 return h
8 8Emp1 = Emp(12345, "Ada")
9 9print("Hash Value of object1 {}".format(hash(Emp1)))
1010Emp2 = Emp(12346, "Ritchie")
1111print("Hash Value of object2 {}".format(hash(Emp2)))
输出:
1Hash Value of object1 8750039985873350939
2Hash Value of object2 -1514168357917997398