在Python-Hash函数中使用对象作为字典中的键
我试图使用对象作为Python中字典的键值。我遵循其他一些帖子的建议,我们需要实现两个函数:hash和eq 有了这些,我希望下面的方法能起作用,但它没有起作用在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
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,但生活中总会有例外。谢谢!你的例子对我帮助很大。你使用的是什么版本的Pythonreturnstr(self.name)==str(other,name)
显然在str(other,name)
中有一个bug,我不明白这是怎么产生的。即使它是str(other.name)
它仍然不能工作,因为hash(str('abc'))
没有.name
属性