2016-10-20 3 views
0

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), но, возможно, я чего-то не хватает.

+0

Требование «hashable», которое [определено] (https://docs.python.org/3/glossary.html#term-hashable) как * «Объект hashable, если он имеет значение хэша, которое никогда не меняется в течение жизни [...] "*. Таким образом, вы нарушаете требование. Ваш * "hashable-but-mutable" * является оксюмороном. –

+0

Обычно мы пытаемся заставить IPython работать со сломанным пользовательским кодом, поэтому не стесняйтесь сообщать о проблеме. Мы можем спокойно взглянуть на это, но ;-) –

+0

@StefanPochmann - возможно, это было то, что я искал - если я нарушаю требование, тогда это нормально, чтобы получить неопределенное поведение. Я не понимал, что это точное определение хэшибл. Благодаря! – dwanderson

ответ

0

Для полноты картины, я помещу здесь ответ, чтобы мы могли закрыть этот цикл:

Начало моего вопроса сказал

«Dict-ключи должны, вероятно, быть неизменны, но это на самом деле не требование . Все, что требуется, это то, что ключ может быть хэширован."

Но, как отметил @Stefan Pochmann, что не достаточно точны, чтобы быть точным. ДИКТ-ключ должен быть hashable и хэш никогда не должны изменяться в течение всего срока службы объекта.

Это не означает объект должен быть неизменным, но это не означает, части объекта, питающие в хэш не должна изменяться таким образом, чтобы они изменить выход __hash__() вызова.

Итак, когда я изменил свой экземпляр таким образом, чтобы изменить хэш, я нарушил требование и все ставки отключены - я больше не могу оправдываться, как этот объект будет действовать. «Проблема» с IPython - это просто вопрос о том, чтобы сделать допустимые предположения об объектах, которые я нарушил (предположения, а не объекты), поэтому для меня совершенно понятно, что это ошибка на мне.