【问题标题】:How should hash be implemented in a user defined Python class? [duplicate]在用户定义的 Python 类中应该如何实现散列? [复制]
【发布时间】:2020-08-17 22:45:18
【问题描述】:

这样做的目的是预测列表切片和比较的结果 在具有用户定义对象的更复杂的项目中。我以为效果如果不是 覆盖 hash 函数的目的是影响这些结果,但它没有 在这里和在这里所做的那样,尚不清楚它是如何做到的。如果 eq 被覆盖,则覆盖 hash 函数必须存在,但它可以返回 'rhubarb' 并且仍然不会影响 这里的结果。由于比较是由 eq 完成的 hash 函数,它的返回值实际使用的方式是什么?

class Myobj:
    def __init__(self,name,suffix='xx', age=21):
        self.name=name
        self.age=age
        self.suffix=suffix
        self.handle=self.name +self.suffix
    def __eq__(self,other):     
        return self.name==other.name and  self.age==other.age   #returns bool
    def __hash__(self):     
        return hash(self.suffix)    #or any or all of name,age,suffix or anything - no difference
    def __repr__(self):
        return self.name
    def __str__(self):
        return f'{self.handle}'

a=Myobj('one')
b=Myobj('two',suffix='yy')
c=Myobj('three')
d=Myobj('four')
e=Myobj('one',age=10)
g=Myobj('one',suffix='yy')

 #with __eq__ and __hash__ overriden
print([a,b,c,d,e])      #[one, two, three, four, one]
print(a,b)              #onexx twoyy
print()
print(f' a=c? {a==c}')  #returns False, names are not=
print(f' a=e? {a==e}')  #returns False, ages  are not=
print(f' a=g? {a==g}')  #returns True, names=, ages= but self.suffix!=other.suffix
print(hash(a),hash(g))  #791158507 -1150071058
print(hash(a.suffix))   #791158507
print(hash('xx'))       #791158507
print(Myobj.__hash__(a)) #791158507
print(set([a,b,c,d,e,g]))   #{one, one, two, one, four, three}

# now with default hash and eq dunders
# print([a,b,c,d,e])        #[one, two, three, four, one]
# print(a,b)                #onexx twoyy
# print()
# print(f' a=c? {a==c}')    #returns False
# print(f' a=e? {a==e}')    #returns False
# print(f' a=g? {a==g}')    #returns False
# print(hash(a),hash(g))    #1463830 1463857
# print(hash(a.suffix))     #-819204916
# print(hash('xx'))         #-819204916
# print(Myobj.__hash__(a))  #1463830

【问题讨论】:

    标签: python hash


    【解决方案1】:

    编辑:我猜你错过了自定义散列函数不会改变程序输出的观点,但它可能会影响性能。考虑这个哈希函数:

        def __hash__(self):     
            return 0
    

    这是最坏的情况。所有对象都返回相同的哈希值。这会影响性能,但除此之外一切都会正常。

    In [1]: class A: 
       ...:     def __hash__(self): 
       ...:         return 0 
       ...:                                                                                                                                                                                               
    
    In [6]: huge_dict = {A():1 for _ in range(10_000)}                                                                                                                                                    
    
    In [7]: a = A()                                                                                                                                                                                       
    
    In [8]: huge_dict[a] = 5                                                                                                                                                                              
    
    In [9]: %timeit huge_dict[a]                                                                                                                                                                          
    211 µs ± 50.6 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    
    In [10]: class B(): 
        ...:     pass 
        ...:                                                                                                                                                                                              
    
    In [11]: huge_dict_better_hash =  {B():1 for _ in range(10_000)}                                                                                                                                      
    
    In [12]: b = B()                                                                                                                                                                                      
    
    In [13]: huge_dict_better_hash[b] = 5                                                                                                                                                                 
    
    In [14]: %timeit huge_dict_better_hash[b]                                                                                                                                                             
    42.7 ns ± 1.43 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
    
    In [20]: f"better hash was {(211*10**-6)/(42.7*10**-9)} times faster"                                                                                                                                 
    Out[20]: 'better hash was 4941.451990632318 times faster
    

    既然比较是由 eq 完成的,那么散列函数的目的是什么?它的返回值实际使用的方式是什么?

    这是一个基本问题why do we need hashing in the first place

    学习how are dicts and sets (hash tables) implemented将帮助你理解哈希

    这里是how to implement __hash__ the right way for a custom class

    【讨论】:

    • 哇。经过几天对这个主题的阅读、试验和错误以及撰写我的问题的相当多的努力后,它在一个小时内没有提及就关闭了。 RafalS,谢谢你,是的,我读过你三篇中的两篇。
    • 我编辑了答案,如果它回答了你的问题,请告诉我。
    猜你喜欢
    • 2017-05-09
    • 2021-07-10
    • 2019-10-23
    • 2023-03-24
    • 2011-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-18
    相关资源
    最近更新 更多