zoukankan      html  css  js  c++  java
  • Python中hash的问题

    ref:http://heipark.iteye.com/blog/1743819

    在下面这个例子里:

    class Item(object):  
      
        def __init__(self, foo, bar):  
            self.foo = foo  
            self.bar = bar  
          
        def __repr__(self):  
            return "Item(%s, %s)" % (self.foo, self.bar)  
          
    print set([Item('1', '2'), Item('1', '2')])  
      
    # 输出: set([Item(1, 2), Item(1, 2)]) 

    逻辑上讲,set中的两个对象是貌似相同的,那么set中应该只有一个对象

    实际上不是这样

    set是根据两个元素的hash value判断这两个对象是不是相同的。元素的hash value是通过hash方法得到的(内部__hash__() magic method)。

    根据文档:

    All of Python’s immutable built-in objects are hashable; mutable containers (such as lists or dictionaries) are not. Objects which are instances of user-defined classes are hashable by default. They all compare unequal (except with themselves), and their hash value is derived from their id().

    可知道只有非可变对象才可hash,并且instances of user-defined classes的hash value是根据他们的id得到的。这个id(ref:https://docs.python.org/3/library/functions.html#id),可以理解为对象在内存中的地址,所以例子里的输出就不奇怪了

    关于__hash__()的自定义实现,文档(ref:https://docs.python.org/3/reference/datamodel.html#object.__hash__)是这么说的:

    it is advised to mix together the hash values of the components of the object that also play a part in comparison of objects by packing them into a tuple and hashing the tuple.

    并且举了一个例子:

    def __hash__(self):
        return hash((self.name, self.nick, self.color))

    这里再引入一个概念:hashable,文档是这么写得:

    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__() method). Hashable objects which compare equal must have the same hash value.

    简单来说,hashable的对象必须实现__hash__ and __equal__两个方法

    我们之前说过了hash方法怎么实现,但是仅仅实现hash方法,是不能让刚开始的例子中输出正确的结果的。原因如下(ref:https://docs.python.org/3/reference/datamodel.html#object.__hash__):

    If a class does not define an __eq__() method it should not define a __hash__() operation either; if it defines __eq__() but not __hash__(), its instances will not be usable as items in hashable collections. If a class defines mutable objects and implements an __eq__() method, it should not implement __hash__(), since the implementation of hashable collections requires that a key’s hash value is immutable (if the object’s hash value changes, it will be in the wrong hash bucket).

    如果定义了eq,没有定义hash,那么显然,由于hash value不同,刚开始的例子中输出结果是错误的

    定义hash的同时要定义eq

    之所以要定义eq,是为了处理set中有两个对象的hash value相同,这时候要怎样处理。

    eq不是每次把元素放进set里都要调用的。如果某个元素和set中的已有元素的hash value都不同,那就没有调用eq的必要了。如果即使两个元素的hash value不同,也要调用eq的话,就失去了hash的意义

    所以在刚开始的例子里加上:

        ...
        def __eq__(self, other):  
            if isinstance(other, Item):  
                return ((self.foo == other.foo) and (self.bar == other.bar))  
            else:  
                return False  
          
        def __hash__(self):  
            return hash(self.foo + " " + self.bar) 
        ...

    就ok了

  • 相关阅读:
    2018 ACM 网络选拔赛 徐州赛区
    2018 ACM 网络选拔赛 焦作赛区
    2018 ACM 网络选拔赛 沈阳赛区
    poj 2289 网络流 and 二分查找
    poj 2446 二分图最大匹配
    poj 1469 二分图最大匹配
    poj 3249 拓扑排序 and 动态规划
    poj 3687 拓扑排序
    poj 2585 拓扑排序
    poj 1094 拓扑排序
  • 原文地址:https://www.cnblogs.com/geeklove01/p/Python.html
Copyright © 2011-2022 走看看