Dict-ключи, вероятно, должны быть неизменяемыми, но это не фактически a требование. Все, что требуется, - это то, что ключ может быть хэширован. Что произойдет, если я изменю объект таким образом, чтобы его хеш изменился после вставки его в мой dict? Помимо просто «плохих вещей», я получаю неожиданное поведение.IPython, изменяемые ключи dict, возможное безумие
>>> class Foo(object):
def __init__(self, n):
self.n = n
def __hash__(self):
return self.n
>>> foo = Foo(1)
>>> d = {foo : foo.n}
>>> print(d)
{<__main__.Foo at 0xdeadbeef: 1}
>>> d
{<__main__.Foo at 0xdeadbeef: 1}
до сих пор, так хорошо. Время для следующих правил закончено. Давайте делать что-то немое?:
>>> foo.n += 1
Теперь я бег IPython 5.1.0
(устанавливается через anaconda
), который работает Python 3.5.2
(на Mac не уверен, но которые деталь системы интересна - попросить больше информации и я будем рады добавить его).
>>> print(d)
{<__main__.Foo at 0xdeadbeef: 1}
>>> d
KeyError ...
IPython/core/formatters.py
print.pretty(obj)
IPython/lib/pretty.py
return self.type_pprinters[cls](obj, self, cycle)
IPython/lib/pretty.py
p.pretty(obj[key])
KeyError: <__main__.Foo at 0xdeadbeef>
Это удивительно/запутанным для меня - если мы сможем правильно print
объект, то почему не может IPython
фигура, как print
это? Кажется, он пытается найти ключ, который, конечно, не может найти, потому что хеш изменился, но - почему тогда print(d)
работает нормально?
Хорошо, не сделали быть немым:
>>> d[foo] = foo.n
Логически мышление - хэш foo
изменился, поэтому он не будет признать, что «уже есть этот ключ» - это не уже есть это ключ. И:
>>> print(d)
{__main__.Foo at 0xdeadbeef: 1, __main__.Foo at 0xdeadbeef: 2}
, но затем, попросив IPython
показать:
>>> d
{__main__.Foo at 0xdeadbeef: 2, __main__.Foo at 0xdeadbeef: 2}
Может быть немного трудно увидеть среди указателей и dunderscores, но он думает, что ОБА значения в нашем словаре являются 2
. Основываясь на stacktrace выше, я предполагаю, что это связано с тем, что он пытается использовать foo
, на котором есть ссылка, и вместо того, чтобы фактически искать в наших словарных клавишах, он ... думает, что он знает ...? И просто использует ссылку (для нашего текущего foo
, с foo.n=2
, и он «знает» значения: foo.n
не «обычные целые числа»)? Это, вероятно, самая сложная часть, и я был бы признателен за понимание.
Заключительный вопрос: это ошибка в IPython (в этом случае я попытаюсь записать отчет об ошибке), или это процесс использования хешируемых, но изменяемых ключей dict, и их изменение и повторное добавление к словари «неопределенное поведение» в Python? Это выглядит довольно четко в терминах вывода от print(d)
, но, возможно, я чего-то не хватает.
Требование «hashable», которое [определено] (https://docs.python.org/3/glossary.html#term-hashable) как * «Объект hashable, если он имеет значение хэша, которое никогда не меняется в течение жизни [...] "*. Таким образом, вы нарушаете требование. Ваш * "hashable-but-mutable" * является оксюмороном. –
Обычно мы пытаемся заставить IPython работать со сломанным пользовательским кодом, поэтому не стесняйтесь сообщать о проблеме. Мы можем спокойно взглянуть на это, но ;-) –
@StefanPochmann - возможно, это было то, что я искал - если я нарушаю требование, тогда это нормально, чтобы получить неопределенное поведение. Я не понимал, что это точное определение хэшибл. Благодаря! – dwanderson