Словарь вызывает KeyError при поиске, даже если ключ на самом деле установлен - PullRequest
1 голос
/ 08 мая 2019

Когда я пытаюсь напечатать словарь (или его __dict__), возникает KeyError.Дикт является отображением от MyClass экземпляров до чисел с плавающей точкой и целых.Было бы слишком долго обмениваться исходным кодом 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) не содержит дубликатов - правильно проверенные экземпляры списка (d) не имеют заметной разницы (некоторые даже являются экземплярами одного и того же класса) - элемент списка

Я не могу вспомнитьобъяснение этого поведения.Это проявилось только сегодня.Недавно я внес некоторые изменения в файловую структуру модуля, содержащего MyClass.До: MyClass подклассы в том же файле, что и определение MyClass.После: MyClass подклассы в отдельных файлах в одном каталоге.Я не понимаю, как это может повлиять на это, но кто знает.

Ответы [ 2 ]

1 голос
/ 08 мая 2019

При нормальных обстоятельствах этого не должно происходить.

Однако вы можете нарушить контракт словаря, согласно которому ключи должны быть неизменными в отношении равенства и хэша.Потому что, если хэш изменяется, объекты больше не могут быть найдены.

Типы отображения - 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__ во время выполнения программы.

0 голосов
/ 08 мая 2019

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), а затем преобразую его в 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}

Также 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
...