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

Python 即使实际设置了键,字典也会在查找时引发KeyError,python,python-3.x,class,dictionary,keyerror,Python,Python 3.x,Class,Dictionary,Keyerror,当我试图打印字典(或其\uuuuuuu dict\uuuuu)时,会出现一个KeyError。dict是从MyClass实例到float和int的映射。 共享MyClass的源代码太长 d = dict() d[MyClass()] = 10.023 d[MyClass()] = 1.023 d[MyClass()] = 8 print(d) Out[16]: --------------------------------------------------------------------

当我试图打印字典(或其
\uuuuuuu dict\uuuuu
)时,会出现一个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>
尽管在引发错误之前,某些值已正确打印。它似乎很难找到一些特定的键

我觉得这也很奇怪:

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
list(d)#工作正常,就像d.keys()和d.values()一样
输出[19]:[,,…]
d[list(d)[0]#工作正常
Out[20]:10.023
d[列表(d)[1]#引发关键错误
d#中的列表(d)[1]返回False
d#中的列表(d)[0]返回True
我检查: -所有键都有不同的散列 -列表(d)不包含重复项 -列表(d)正确查找的实例没有明显差异(有些甚至是同一类的实例) -列表项

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

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

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)
,然后将其转换为dict

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}
更新:如果我使用更简单的类,您新更新的语法也适用于我

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
A类:
通过
d=dict()
d[A()]=10.023
d[A()]=1.023
d[A()]=8
印刷品(d)
#{: 10.023, : 1.023, : 8}
对于输入d:
打印(d[键])
#10.023
#1.023
#8

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

但是,您可能会违反字典约定,即键在相等和哈希方面必须是不可变的。因为如果散列改变了,就再也找不到对象了

[……]

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

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

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>
>t1=测试(1)
>>>d={}
>>>d[t1]=10
>>>t1.value=2#t1的散列在这一点上也会发生变化
>>>d[t1]
关键错误:

这当然过于简化了,但我怀疑在您的代码中会发生类似的情况。我建议您在程序执行过程中监控与
\uuuuuu hash\uuuuu
\uuuuuuu eq\uuuuu
相关的值的更改。

更新了我的答案,如果您遵循我在答案中使用的约定,您显示的所有示例都适用于我,请检查@user3886973?问题中的更新代码也适用于我@user3886973是的!好的。但这是否也解释了在d.keys()方法中所有键如何保持可访问性?这真的让我很困惑。事实上,散列的一个不应该改变的部分正在被改变。臭虫找到了!Thanks@user3886973键值对仍在字典中。只是lookup计算“当前”散列,并将该散列与在字典中插入密钥时计算的散列进行比较。如果这一点发生变化,则查找将失败。在迭代和
.keys
过程中,它只返回所有存储的键值对(不考虑散列)。@user3886973我很高兴答案为您指明了正确的方向,并感谢您接受答案。请不要忘记所有你认为有用的答案。
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>