Python 在字典键中,对象不被认为是相同的,但是_eq___________________________
以下代码给出了一条错误消息:Python 在字典键中,对象不被认为是相同的,但是_eq___________________________,python,dictionary,Python,Dictionary,以下代码给出了一条错误消息: class Test(object): def __init__(self, test = 0): self.test = test if __name__ == '__main__': t1 = Test(1) t2 = Test(2) t3 = Test(3) t4 = Test(1) my_dict = {} my_dict[t1] = 1 my_dict[t2] =
class Test(object):
def __init__(self, test = 0):
self.test = test
if __name__ == '__main__':
t1 = Test(1)
t2 = Test(2)
t3 = Test(3)
t4 = Test(1)
my_dict = {}
my_dict[t1] = 1
my_dict[t2] = 2
my_dict[t3] = 3
print(my_dict[t4])
Traceback (most recent call last):
File "C:\Users\Alexander\Documents\Visual Studio 2015\Projects\PipeProcessTest
\PipeProcessTest\DictionaryKeys.py", line 16, in <module>
print(my_dict[t4])
KeyError: <__main__.Test object at 0x0000000002F18080>
我收到另一条错误消息,“unhabable type:'Test'”告诉我,现在字典无法散列测试对象。如何解决这个问题,使Python能够识别t1和t4是相同的,但也能够对测试对象进行哈希处理?除了
\uuuuuueq\uucode>之外,还需要实现\uuuuuuhash\uucode>。有关如何执行此操作的说明,请参见。要记住的主要一点是,比较相等的对象必须具有相同的哈希值。因此,如果您只想通过查看test
属性来比较相等性,那么您的\uuuuuuuuuuuuuuuuuuu
也只需要使用test
属性。搜索有关\uuuuuuuuuuuuuuu散列和\uuuueq\uuuuuuuuuuuuuuuuuuuuuuuu
的信息也会在这个网站上发现许多以前的问题。你只需要返回方法中的self.test
,这样你的对象散列值就基于它们的测试属性,所以t1和t4将具有相同的散列值和dict在t1或t4上查找将返回相同的值:
def __init__(self, test = 0):
self.test = test
def __eq__(self, other):
return self.test == other.test
def __hash__(self):
return self.test
如果在eq
中不需要if/else,您只需返回self.test==other.test
In [2]: t1 = Test(1)
In [3]: t2 = Test(2)
In [4]: t3 = Test(3)
In [5]: t4 = Test(1)
In [6]: my_dict = {}
In [7]: print(t1 == t4)
True
In [8]: my_dict[t1] = 1
In [9]: my_dict[t2] = 2
In [10]: my_dict[t3] = 3
In [11]: print(my_dict[t4])
1
1。这是因为python将t1和t4视为不同的对象。但是,当我使用以下代码实现比较运算符“eq”时:
在python中执行以下操作时
class Test(object):
def __init__(self, test = 0):
self.test = test
if __name__ == '__main__':
t1 = Test(1)
t2 = Test(2)
t3 = Test(3)
t4 = Test(1)
my_dict = {}
my_dict[t1] = 1
my_dict[t2] = 2
my_dict[t3] = 3
这意味着您实际上正在尝试创建一个dict
,其中键是Test
对象,Python首先检查键是否可散列
Python中的任何对象
在返回obj的任何整数
值时都是可哈希的。在python中,默认情况下,所有用户定义的类都会获取一些散列值,即id(self)
很明显,当你得到id
值作为hash
值时,它们看起来像这个值8772302607193
。因此,如果我们使用这些id构造哈希表
表,它可能如下所示
id(t1) = 1
id(t2) = 4 # These are just assumptions.
id(t3) = 10 # actual id's are long values.
id(t4) = 20
让我们假设id是这样的
id(t1) = 1
id(t2) = 4 # These are just assumptions.
id(t3) = 10 # actual id's are long values.
id(t4) = 20
这就是hash
表的构造方式
hash Actual
value value
------------------
| 1 | t1 |
------------------
| 2 | NULL |
------------------ # Consider that you've respective values are
| 3 | NULL | # here, just for representation I've mentioned
------------------ # t1, t2, t3, t4, ..
| 4 | t2 |
------------------
|
|
------------------
| 10 | t3 |
------------------
|
SO ON
像这样,您的散列
表就被构造出来了,所以当您尝试获取t4的值时,只需尝试my_dict[t4]
。根据假设t4
哈希值为20
,通过调用t4.\uu hash\uuuu()
,python首先检查t4
的哈希值
在获得哈希值20
后,它将检查索引为20的哈希表,因为我们没有使用20
插入任何值,Python只会引发KeyError
异常,这就是您在尝试my_dict[t4]
时得到KeyError
的原因
这里还有另一个场景:
如果您尝试重写\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
class Test(object):
def __init__(self, test = 0):
self.test = test
def __hash__(self):
self.test # You are just returning the same value
if __name__ == '__main__':
t1 = Test(1)
t2 = Test(2)
t3 = Test(3)
t4 = Test(1)
my_dict = {}
my_dict[t1] = 1
my_dict[t2] = 2
my_dict[t3] = 3
由于我们重载了
hash方法以返回与初始化时相同的值,下面是我们得到的hash值
t1 = 1 , t2 = 2, t3 = 3, t4 = 1
这就是当我们有多个具有相同哈希值的值时,hash
表的外观
hash Actual
value value
------------------
| 1 | [t1,t4] | # List of values for the same hash value.
------------------
| 2 | t2 |
------------------ # Consider that you've respective values are
| 3 | t3 | # here, just for representation I've mentioned
------------------ # t1, t2, ...
| 4 | NULL |
------------------
|
SO ON
在这种情况下,当您尝试获取my_dict[t4]
时,如前所述,首先检查哈希值t4。
返回1
。现在,Python在哈希表中的索引1处进行dict检查,它得到多个值[t1,t4]
在这种情况下,\uuuuueq\uuuu
可以帮助您识别具有相同哈希值的多个值的对象。你可以像下面这样避免这种情况
class Test(object):
def __init__(self, test = 0):
self.test = test
def __hash__(self):
return self.test
def __eq__(self, other):
return self is other
在您的情况下,只需验证self.test
值即可获得对象
class Test(object):
def __init__(self, test = 0):
self.test = test
def __hash__(self):
return self.test
def __eq__(self, other):
return other.test == self.test
这就是如何管理dict值的方法 对于每个人来说,这里有一个相当不错的例子:
class NonComparable(object):
def __init__(self):
pass
# Convert a cmp= function into a key= function
class Comparable(object):
def __init__(self, val, hash_lambda, cmp_lambda):
self.val = val
self.hash_lambda = hash_lambda
self.cmp_lambda = cmp_lambda
def __hash__(self):
return self.hash_lambda(self.val)
def __eq__(self, other):
return self.cmp_lambda(self.val, other.val) == 0
# return id(self) == id(other)
# driver code
if __name__ == "__main__":
# __hash__ is available even if you don't implement it, it'd return hash(nc1), not id(nc1). hash() isn't available by default.
# Note also,
# id(123) = an integer
# hash(456) = 456
# So, hash(id(nc1)) = id(nc1)
nc1 = NonComparable()
nc1_hash = nc1.__hash__()
nc2 = NonComparable()
nc2_hash = nc2.__hash__()
moduli = 2
hash_lambda = lambda x : x * x
cmp_lambda_by_val = lambda x,y : x == y
cmp_lambda_by_objid = lambda x,y : id(x) == id(y)
c1 = Comparable(5, hash_lambda, cmp_lambda_by_val) # hash = 5*5 = 25
c2 = Comparable(8, hash_lambda, cmp_lambda_by_val) # hash = 8*8 = 64
# same hash code (To simulate hash collision), but compare by Equal based on id(obj)
c3 = Comparable(5, hash_lambda, cmp_lambda_by_objid)
c4 = Comparable(5, hash_lambda, cmp_lambda_by_objid)
dict1 = {}
dict1[nc1] = nc1
dict1[nc2] = nc2
res = dict1[nc1]
dict2 = {}
dict2[c1] = c1
dict2[c2] = c2
res = dict2[c1]
dict3 = {}
dict3[c3] = c3
dict3[c4] = c4
res = dict3[c3]
# id(c3) == id(res)
pass
参考资料
你知道你从来没有把t4
放在你的字典里,对吧?@MorganThrapp,我想这就是问题所在,如何用t4
进行查找以返回t1,因为它们都是用1
初始化的@PadraicCunningham ahh,这稍微有点道理。我也不明白反对票——你的解决方案应该有效。其实布伦巴恩也说过同样的话。也许是因为你没有解释背后的原因?(只是猜测)。@TigerhawkT3,是的,修正了。@Iserni,可能是这样,但dv几乎是瞬间的,所以可能有其他力量在起作用!