Почему кортеж со списком не может быть ключом в словаре?

Рейтинг: 3Ответов: 1Опубликовано: 29.03.2023

Есть вот такой код

ti = {
    'ke': 213, 
    31: 'zna',
    (1, 2, 3): 'kort',
}

#   {'ke': 213, 31: 'zna', (1, 2, 3): 'kort'}

С ним всё в порядке. Ключи подходят и всё прочее, но когда я пытаюсь сделать:

ti = {
    'ke': 213, 
    31: 'zna',
    (1, [1, 2], 3): 'kort',
}

Я получаю ошибку:

TypeError: unhashable type: 'list'

Я понимаю, что ключами должны быть неизменяемые типы, которые являются хешируемыми. И кортеж я могу использовать при условии, что всё внутри него будет неизменяемым. А список не может быть отхеширован. Но мы же по сути не можем изменить кортеж.

Почему принципиальны его внутренности? Я же не могу поменять его содержимое

P.S. Странности ещё добавляет тот факт, что у списка есть метод __hash__

print(dir(list('dewf')))


#  ...'__gt__', '__hash__', '__iadd__',...

Цитата из документации:

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

Но в этом и суть вопроса. Я ведь не могу изменить кортеж

a = [1, 2]

kort = (1, a, 4) 

print(kort) #(1, [1, 2], 4)

a = [5, 6] 

print(kort) #(1, [1, 2], 4)

Ответы

▲ 3Принят

Есть простой принцип, который может не объясняет данного поведения, но дает представление, что можно использовать в виде ключа в словаре.

Только неизменяемый (immutable) тип данных можно использовать, как ключ в словаре

Есть типы данных, которые являются не изменяемыми (str, int и проч.), то есть по той ссылке, которая хранится в переменной/ключе вы не сможете изменить данные. Что, скорей всего и есть правильное применение для ключа.

FAQ в официальной документации содержит ответ на данный вопрос здесь.

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

Не очень понимаю, практическое применение вопроса, если только не разобраться во внутренней кухне языка.

По комментариям под вопросом. Вы можете поменять список в кортеже

>>> a = (1, 2, []) 
>>> a[2] 
[]
>>> a[2].append(2) 
>>> a[2] 
[2]
>>> a[2].append(100) 
>>> a[2]
[2, 100]

PS: можно перевести ответ из офдоки, когда будет время