以下代码给出了错误消息:
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>
这是因为python将t1和t4视为不同的对象。但是,当我使用以下代码实现比较运算符&#39; eq &#39;时:
def __eq__(self, other):
if self.test == other.test:
return True
else:
return False
我收到另一条错误消息,&#34;不可用类型:&#39;测试&#39;&#34;告诉我,现在字典不能散列Test对象。我如何解决这个问题,以便Python能够识别t1和t4是否相同,还能够散列Test对象?
答案 0 :(得分:7)
除了__hash__
之外,您还需要实施__eq__
。有关如何执行此操作的说明,请参阅the documentation。要记住的主要事情是比较相等的对象必须具有相同的哈希值。因此,如果您只想通过查看test
属性来比较相等性,那么您的__hash__
也只需要使用test
属性。搜索有关__hash__
和__eq__
的信息也会在此网站上发现此前的许多问题。
答案 1 :(得分:4)
<强> 1。这是因为python将t1和t4视为不同的对象。但是,当我使用以下代码实现比较运算符&#39; eq&#39;
当您在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会首先检查keys
是否为hashable
。
Python中的任何object
在hashable
方法返回任何integer
值时都为obj.__hash__()
。在python中,默认情况下,所有用户定义的类都会获得一些id(self)
的哈希值。
显然,当您获得id
值为hash
时,它们会看起来像这个值8772302607193
。如果我们构建hash
表,那么这些id可能就像这样......
让我们假设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
像这样,您的hash
表格会被构建,所以当您尝试获取t4
的值时,只需尝试my_dict[t4]
。根据假设hash
哈希值为t4
,首先通过调用t4.__hash__()
检查t4
20
值20
。
获取哈希值20
后,它检查哈希表,索引为20,因为我们没有用KeyError
插入任何值,Python只会引发KeyError
异常,这是您在尝试my_dict[t4]
时获得__hash__
的原因。
此处的另一个方案:
如果您尝试覆盖Test
方法f 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
类并继续执行下面所做的相同操作..
overloaded
由于我们用t1 = 1 , t2 = 2, t3 = 3, t4 = 1
哈希方法返回与初始化相同的值,下面是我们得到的哈希值
hash
当我们使用same hash value
设置多个值时,这就是 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.__hash__()
时,如第一次检查哈希值1
之前所述,返回index
。现在,在hash table
的{{1}} 1处进行Python dict检查,它会获得多个值[t1, t4]
。
当您使用相同__eq__
的多个值时,hash value
可帮助您识别对象。你可以这样做,以避免这种情况......
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值的方法!
答案 2 :(得分:3)
您只需要在self.test
方法中返回__hash__
,以便您的对象哈希值基于其测试属性,因此t1和t4将具有相同的哈希值并且在t1或t4上具有dict查找将返回相同的值:
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
答案 3 :(得分:0)
对于每个人,这是一个不错的示例:
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
参考
Objects are not considered the same in Dictionary keys - but __eq__ is implemented
https://www.programiz.com/python-programming/methods/built-in/hash
https://dbader.org/blog/python-dictionaries-maps-and-hashtables