【问题标题】:Using object as key in dictionary in Python - Hash function在 Python 中使用对象作为字典中的键 - 哈希函数
【发布时间】:2013-06-30 20:24:36
【问题描述】:

我正在尝试使用一个对象作为 Python 中字典的键值。我遵循其他一些帖子的建议,我们需要实现 2 个功能:__hash____eq__

有了这个,我期待以下工作,但它没有。

class Test:
    def __init__(self, name):
        self.name = name
        
    def __hash__(self):
        return hash(str(self.name))
        
    def __eq__(self, other):
        return str(self.name) == str(other,name)
        
        
def TestMethod():
    test_Dict = {}
    
    obj = Test('abc')
    test_Dict[obj] = obj
    
    print "%s" %(test_Dict[hash(str('abc'))].name)       # expecting this to print "abc" 

但它给了我一个关键的错误信息:

KeyError: 1453079729188098211

【问题讨论】:

  • 您使用的是什么版本的 Python? return str(self.name) == str(other,name) 显然在str(other,name) 中有一个错误,我不明白这是如何不提出来的。即使它是str(other.name),它仍然无法工作,因为hash(str('abc')) 没有.name 属性

标签: python object dictionary key


【解决方案1】:

映射的元素通过它们的散列访问,即使它们的散列用于将它们放置在映射中。您必须在索引存储和检索时使用相同的值。

【讨论】:

    【解决方案2】:

    您无需重新定义 hasheq 即可将对象用作字典键。

    class Test:
        def __init__(self, name):
            self.name = name
    
    test_Dict = {}
    
    obj = Test('abc')
    test_Dict[obj] = obj
    
    print test_Dict[obj].name
    

    这工作正常并打印abc。 正如 Ignacio Vazquez-Abrams 所解释的,您不使用对象的哈希,而是使用对象本身作为访问字典值的键。


    您找到的示例如 python: my classes as dict keys. how?Object of custom type as dictionary key 为特定目的重新定义了 hasheq

    例如考虑这两个对象obj = Test('abc')obj2 = Test('abc')

    test_Dict[obj] = obj
    print test_Dict[obj2].name
    

    这将引发KeyError 异常,因为 obj 和 obj2 不是同一个对象。

    class Test:
        def __init__(self, name):
            self.name = name
    
        def __hash__(self):
            return hash(str(self.name))
    
        def __eq__(self, other):
            return str(self.name) == str(other.name)
    
    obj = Test('abc')
    obj2 = Test('abc')       
    
    test_Dict[obj] = obj
    print test_Dict[obj2].name
    

    此打印abcobjobj2 仍然是不同的对象,但现在它们具有相同的哈希值,并且在比较时被评估为相等。

    【讨论】:

    • 谢谢!你的例子对我帮助很大。
    【解决方案3】:

    错误说明

    鉴于帖子中提供的代码,我实际上并没有看到您是如何获得 KeyError 的,因为您应该收到一个 AttributeError(假设 str(other,name) 是一个错字,应该是 str(other.name))。在将 self 的名称与 other 的名称进行比较时,AttributeError 来自 __eq__ 方法,因为您在查找期间的键 hash(str('abc')) 是 int/long,而不是 Test 对象。

    在字典中查找键时,执行的第一个操作是使用键的__hash__ 方法获取键的哈希值。其次,如果 dict 中存在此哈希的值,则调用键的 __eq__ 方法将键与找到的任何值进行比较。这是为了确保在具有相同哈希的事件对象存储在字典中(通过开放寻址)时,检索到正确的对象。平均而言,此查找仍然是 O(1)。

    一步一步看,hash(str('abc'))obj 的哈希值是相同的。在Test 中,您将__hash__ 定义为字符串的哈希值。当使用test_Dict[hash(str('abc'))] 执行查找时,实际上是在查找哈希的哈希,但这仍然没问题,因为 int 的哈希本身在 python 中。

    根据您定义的__eq__ 方法比较这两个值时,您比较对象的名称,但您要比较的值是一个 int (hash(str('abc'))),它没有 name 属性,因此引发 AttributeError。

    解决方案

    首先,在执行实际的 dict 查找时,您不需要(也不应该)调用 hash(),因为该键也作为第二个参数传递给您的 __eq__ 方法。所以

    test_Dict[hash(str('abc'))].name
    

    应该变成

    test_Dict[str('abc')].name
    

    或者只是

    test_Dict['abc'].name
    

    因为在字符串文字上调用 str() 没有多大意义。

    其次,您需要编辑您的 __eq__ 方法,使其考虑到您正在比较的 other 对象的类型。您对此有不同的选择,具体取决于您将 Test 实例作为键存储在同一字典中的其他内容。

    • 如果您打算将您的 Test 实例仅与其他 Tests(或任何具有 name 属性的对象)一起存储在字典中,那么您可以保留当前拥有的内容

      def __eq__(self, other):
          return str(self.name) == str(other.name)
      

      因为你保证你在字典中比较的所有其他键都是Test 类型并且有一个name

    • 如果您打算将字典中的 Test 实例与字符串混合,则必须检查您比较的对象是否为字符串,因为字符串在 python 中没有 name 属性。

      def __eq__(self, other):
          if isinstance(other, str):
              return str(self.name) == other
          return str(self.name) == str(other.name)
      
    • 如果您打算将Tests 和任何其他类型的对象混合用作键,则需要检查other 对象是否有name 进行比较。

      def __eq__(self, other):
          if hasattr(other, "name"):
              return str(self.name) == str(other.name)
          return self.name == other  # Or some other logic since here since I don't know how you want to handle other types of classes.
      

    我不太喜欢最后两个,因为你有点反对用那些在 python 中输入鸭子,但生活中总会有例外。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-07
      • 2014-09-01
      • 2012-08-31
      • 1970-01-01
      • 1970-01-01
      • 2011-03-29
      • 1970-01-01
      • 2021-12-17
      相关资源
      最近更新 更多