即使实际设置了键,字典也会在查找时引发KeyError

时间:2019-05-08 08:53:34

标签: python python-3.x class dictionary keyerror

当我尝试打印字典(或其__dict__)时,会引发KeyError。 dict是从MyClass实例到float和int的映射。 共享MyClass的源代码太长了。

d = dict()
d[MyClass()] = 10.023
d[MyClass()] = 1.023
d[MyClass()] = 8
print(d)
Out[16]: ---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
c:\users\[...]\lib\site-packages\IPython\core\formatters.py in __call__(self, obj)
    700                 type_pprinters=self.type_printers,
    701                 deferred_pprinters=self.deferred_printers)
--> 702             printer.pretty(obj)
    703             printer.flush()
    704             return stream.getvalue()

[...]

    618             p.pretty(key)
    619             p.text(': ')
--> 620             p.pretty(obj[key])
    621         p.end_group(step, end)
    622     return inner

KeyError: <MyClass.subclass instance>

以下还会引发KeyError:

for key in d:
    print(d[key])

尽管在出现错误之前可以正确打印某些值。查找某些特定键似乎有麻烦。

我认为这也很奇怪:

In [19]: [a in d for a in d]
Out[19]: [False, True, False, True, False]

最后:

list(d) # works fine, just like d.keys() and d.values()
Out[19]: [<MyClass.subclass instance>,<MyClass.subclass instance>,<MyClass.subclass instance>,...]
d[list(d)[0]] # works fine
Out[20]: 10.023
d[list(d)[1]] # raises KeyError
list(d)[1] in d # returns False
list(d)[0] in d # returns True

我检查了:  -所有键都有不同的哈希  -列表(d)不包含重复项  -正确查找的list(d)实例没有明显区别(有些甚至是同一类的实例)  -列表项

我想不出对此行为的解释。它直到今天才显现出来。 最近,我对包含MyClass的模块的文件结构进行了一些更改。之前:MyClassMyClass定义在同一个文件中。 之后:MyClass属于同一目录中各个文件的子类。 我不知道这会如何影响其中的任何一个,但谁知道。

2 个答案:

答案 0 :(得分:1)

在正常情况下,这种情况不会发生。

但是,您可能会违反字典约定,即键必须在相等性和哈希值方面保持不变。因为如果哈希值发生更改,将无法再找到对象。

  

Mapping Types — dict

     

[...]

     

字典的键几乎是任意值。不可散列的值,即包含列表,字典或其他可变类型的值(通过值而不是对象标识进行比较),不能用作键。

要演示当您使用具有依赖于值的哈希函数的可变对象时会发生什么:请考虑此类:

class Test:
    def __init__(self, value):
        self.value = value

    def __hash__(self):
        return self.value

这个小片段:

>>> t1 = Test(1)
>>> d = {}
>>> d[t1] = 10
>>> t1.value = 2  # t1's hash also changes at this point
>>> d[t1]
KeyError: <__main__.Test object at 0x0000020FCA5AD748>

这当然过于简化,但是我怀疑您的代码中会发生类似的事情。我建议您在程序执行期间监视与__hash____eq__相关的值的更改。

答案 1 :(得分:0)

d = dict(MyClass()=10.023, MyClass()=1.023, MyClass()=8)不是创建字典的正确语法。当我尝试按以下方式运行

class A:
    pass

d = dict(A() = 10.023, A() = 1.023, A() = 8)
print(d)

我得到了例外

    d = dict(A() = 10.023, A() = 1.023, A() = 8)
            ^
SyntaxError: keyword can't be an expression

尝试做类似事情的正确示例是,我列出了元组(classobject, value),然后将其转换为字典

class A:
    pass

d = dict([(A(),10.023), (A(),1.023), (A(),8)])
print(d)

输出将为

{<__main__.A object at 0x103072ba8>: 10.023, <__main__.A object at 0x103072be0>: 1.023, <__main__.A object at 0x103072c88>: 8}

list(d)为您提供了一个键列表,就像您在示例中已经看到的那样,然后当您执行d[list(d)[0]]时,您将获得第一个键,然后获得第一个键的值键。

对于最后两个语句,如果您遵循我的命名约定,它们将返回true,同样,您问题中的更新语句也对我有用。

class A:
    pass

d = dict([(A(),10.023), (A(),1.023), (A(),8)])

#Print list of keys
print(list(d))
#[<__main__.A object at 0x102a72ba8>, <__main__.A object at 0x102a72be0>, <__main__.A object at 0x102a72c88>]

#Value of first key
print(d[list(d)[0]])
#10.023

#Value of second key
print(d[list(d)[1]])
#1.023

#Checks if second key is present in list of keys, which is True
print(list(d)[1] in d )
#True

#Checks if first key is present in list of keys, which is True
print(list(d)[0] in d )
#True

更新:如果我使用更简单的类,您的新更新语法也对我有用

class A:
    pass

d = dict()
d[A()] = 10.023
d[A()] = 1.023
d[A()] = 8

print(d)
#{<__main__.A object at 0x101f72be0>: 10.023, <__main__.A object at 0x101f72c88>: 1.023, <__main__.A object at 0x101f7def0>: 8}


for key in d:
    print(d[key])
#10.023
#1.023
#8