Странное поведение словаря, созданного из fromkeys

коллеги! Просветите меня, пожалуйста, за странное поведение аппенда в ключ словаря, созданного из fromkeys:

def hash_as_key(obj):
    x = {}.fromkeys([hash(i) for i in obj], [])
    [x[hash(j)].append(j) for j in obj]
    return {k:v if len(v) != 1 else v[0] for k, v in x.items()}

print(hash_as_key([1, 2, 3, 4, 5, 5]))

Требуемый вывод: {1: 1, 2: 2, 3: 3, 4: 4, 5: [5, 5]}

Фактический вывод: {1: [1, 2, 3, 4, 5, 5], 2: [1, 2, 3, 4, 5, 5], 3: [1, 2, 3, 4, 5, 5], 4: [1, 2, 3, 4, 5, 5], 5: [1, 2, 3, 4, 5, 5]}

Аппендится весь список, вместо конкретного значения из списка. При этом, если создавать словарь через defaultdict - все работает как требуется.


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

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

Совершенно нормальное поведение, строго по докстрингу:

Signature: dict.fromkeys(iterable, value=None, /)
Docstring: Create a new dictionary with keys from iterable and values set to value.

Все значения словаря устанавливаются указанным вами значением. А значение у вас - один конкретный пустой список. В результате, все значения этого словаря указывают на один и тот же список.

Хотите задать в качестве значений разные списки - используйте, например, словарное сокращение (или как это правильно называется):

x = {hash(i):[] for i in obj}

Ну или defaultdict(list), тогда списки тоже будут разные, да.

Да, и зачем вы, собственно, используете hash в явном виде я тоже не понял. Плюсов тут никаких, но есть риск нарваться на коллизию хэшэй. Словарь и так сам под капотом будет использовать hash, а в случае коллизии - явное сравнение объектов, чего у вас и нет. Например, можете глянуть, чему внезапно равен hash(-1), ну и другие интересные случаи могут быть.

→ Ссылка