Изменения в ключе меняют словарь

Есть код, аналогичный данному:

class A:
    def __init__(self, i):
        self.i = i
    def __hash__(self):
        return hash(self.i)
    def __eq__(self, other):
        return self.i == other.i

a = A(1)
m = {a: '1', A(2): '2'}
m[A(1)] # => '1'
a.i = 5
m[A(1)] # KeyError, а хочется '1'

Подскажите, как добиться желаемого поведения? Понятно, что можно использовать что-то такое:

from copy import copy, deepcopy
a = A(1)
m = {copy(a): '1', A(2): '2'}
m[A(1)] # => '1'
a.i = 5
m[A(1)] # '1'

Но выглядит странно и, кажется, должно есть много памяти. Есть ли какое-то стандартное рещение?


Ситуация примерно такая:

m = {i: f(i) for i in some_list}

def get_mutated_value(smt):
    return m[m[smth].mutate()]

Ответы (1 шт):

Автор решения: CrazyElf

Ну, можно вот так легко добиться требуемого поведения в этом конкретном случае, но не очень понимаю, в чём глубокий смысл.

class A:
    def __init__(self, i):
        self.i = i
    def __hash__(self):
        return hash(self.i)
    def __eq__(self, other):
        return True

a = A(1)
m = {a: '1', A(2): '2'}
m[A(1)] # => '1'
a.i = 5
print(m[A(1)]) # => '1'
print(m[A(2)]) # => '2'

Если что, суть тут в том, что хэш объекта считается только в момент помещения элемента в словарь, а потом проверяется только eq.

Вариант реализации решения, предложенного Xander - с сохранением первоначального состояния, так, думаю, более правильно (при всей кривизне ситуации):

class A:
    def __init__(self, i):
        self.i = i
        self._i = i
    def __hash__(self):
        return hash(self._i)
    def __eq__(self, other):
        return self._i == other._i

А, вот вам ещё один смешной вариант, даже не надо менять ваш код класса. Просто используйте в качестве ключа словаря поле класса, а не класс:

a = A(1)
m = {a.i: '1', A(2).i: '2'}
m[A(1).i] # => '1'
a.i = 5
m[A(1).i] # => '1'
→ Ссылка