在Python-Hash函数中使用对象作为字典中的键

在Python-Hash函数中使用对象作为字典中的键,python,object,dictionary,key,Python,Object,Dictionary,Key,我试图使用对象作为Python中字典的键值。我遵循其他一些帖子的建议,我们需要实现两个函数:hash和eq 有了这些,我希望下面的方法能起作用,但它没有起作用 class Test: def __init__(self, name): self.name = name def __hash__(self): return hash(str(self.name)) def __eq__(self, other): retu

我试图使用对象作为Python中字典的键值。我遵循其他一些帖子的建议,我们需要实现两个函数:hasheq

有了这些,我希望下面的方法能起作用,但它没有起作用

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

有人能告诉我为什么这不起作用吗?

映射的元素不被它们的哈希访问,即使它们的哈希被用来将它们放在映射中。为存储和检索编制索引时必须使用相同的值。

要将对象用作字典键,无需重新定义
哈希
eq

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所解释的,您不使用对象的散列,而是使用对象本身作为键来访问字典值


您找到的示例类似于或重新定义了
散列
eq
,用于特定目的

例如,考虑这两个对象<代码> Obj=测试('ABC)和<代码> Obj2=测试(ABC)

由于obj和obj2不是同一个对象,这将引发
KeyError
异常

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
此打印
abc
obj
obj2
仍然是不同的对象,但现在它们有相同的散列,并且在比较时计算结果相等。

错误解释 考虑到帖子中提供的代码,我实际上不知道您是如何得到KeyError的,因为您应该收到AttributeError(假设
str(other,name)
是一个拼写错误,意思是
str(other.name)
)。当比较self的名称和other的名称时,AttributeError来自
\uuuuuu eq\uuuu
方法,因为在查找过程中,键
hash(str('abc'))
是int/long,而不是
测试对象

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

一次看这一步,
hash(str('abc'))
obj
的散列是相同的。在
Test
中,您将
\uuuuuu散列\uuuuuuu
定义为字符串的散列。使用
test_Dict[hash(str('abc'))]
执行查找时,实际上是在查找哈希的哈希,但这仍然可以,因为int的哈希本身在python中

def __eq__(self, other):
    if isinstance(other, str):
        return str(self.name) == other
    return str(self.name) == str(other.name)
当根据定义的
\uuuuueq\uuuuuuu
方法比较这两个值时,可以比较对象的名称,但所比较的值是一个int(
hash(str('abc'))
),它没有
name
属性,因此引发AttributeError

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

test_Dict[hash(str('abc'))].name
应该成为

test_Dict[str('abc')].name
或者只是

test_Dict['abc'].name
因为对字符串文本调用
str()
没有多大意义

其次,您需要编辑
\uuuu eq\uuuu
方法,以便它考虑到您正在比较的
其他
对象的类型。根据
Test
实例作为键存储在同一dict中的其他内容,您有不同的选项

  • 如果您计划将
    Test
    实例仅与其他
    Test
    s(或具有
    名称
    属性的任何对象)一起存储在字典中,则可以保留当前拥有的内容

    def __eq__(self, other):
        return str(self.name) == str(other.name)
    
    因为您保证您在dict中比较的每一个键都是
    Test
    类型,并且具有
    名称

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

    def __eq__(self, other):
        if isinstance(other, str):
            return str(self.name) == other
        return str(self.name) == str(other.name)
    
  • 如果计划将
    Test
    s和任何其他类型的对象混合用作键,则需要检查
    other
    对象是否具有要比较的
    名称

    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键入duck,但生活中总会有例外。

谢谢!你的例子对我帮助很大。你使用的是什么版本的Python
returnstr(self.name)==str(other,name)
显然在
str(other,name)
中有一个bug,我不明白这是怎么产生的。即使它是
str(other.name)
它仍然不能工作,因为
hash(str('abc'))
没有
.name
属性