unhashable тип: ошибка типа 'dict'

Предположим, у меня есть этот словарь:

items = {1: {'title': u'testing123', 'description': u'testing456'},
2: {'description': u'testing123', 'description': u'testing456'},
3: {'description': u'testing123', 'description': u'testing456'},
4: {'description': u'testing123', 'description': u'testing456'},
5: {'description': u'testing123', 'description': u'testing456'},
6: {'description': u'somethingelse', 'description': u'somethingelse'}}

Я хочу отфильтровать повторяющиеся значения, чтобы в итоге получить

{1: {'title': u'testing123', 'description': u'testing456'}, 6: {'title': u'something', 'description': u'somethingelse'}}

Я написал этот код:

dic = {}

for key, value in items.items():
    if not set(value.values()).issubset(set(dic.values())):
        dic[key] = value

однако я получаю сообщение об ошибке TypeError: unhashable type: 'dict'. Я не уверен, почему это происходит и как это исправить.

Это вдохновлено другим вопросом и моей неудачной попыткой решить Это.


person Morteza R    schedule 12.12.2014    source источник


Ответы (3)


dic.values() возвращает список dict

>>> for key, value in items.items():
...     print dic.values()
...
[{'description': u'testing456', 'title': u'testing123'}]
[{'description': u'testing456', 'title': u'testing123'}]
[{'description': u'testing456', 'title': u'testing123'}]
[{'description': u'testing456', 'title': u'testing123'}]
[{'description': u'testing456', 'title': u'testing123'}]
[{'description': u'testing456', 'title': u'testing123'}]
>>>

Таким образом, вы не можете применить set к dict, поскольку dict не хэшируется.

Кстати, вы можете исправить это:

>>> dic = {}
>>> for key, value in items.items():
...     if not set(value.values()).issubset(set(sum([x.values() for x in dic.values()],[]))):
...         dic[key] = value
...
>>> dic
{1: {'description': u'testing456', 'title': u'testing123'}, 6: {'description':     u'somethingelse', 'title': u'somethingelse'}}
>>>

Для питона> 3.x

if not set(value.values()).issubset(set(sum([list(x.values()) for x in list(dic.values())],[]))):
person James    schedule 12.12.2014
comment
Когда я запускаю код, я получаю TypeError: can only concatenate list (not "dict_values") to list. Я на Питоне 3.4 - person Morteza R; 12.12.2014
comment
для python 3.4 попробуйте так: если не установлено (value.values()).issubset(set(sum([list(x.values()) for x in list(dic.values())],[]) )): - person James; 12.12.2014
comment
Хорошо, теперь это работает хорошо. Спасибо! - person Morteza R; 12.12.2014

Изменить: если вы должны использовать набор, как отмечали другие, вы должны использовать хешируемый объект, например кортеж:

unique_items = set()
for k, v in items.items():
    sorted_v = tuple(sorted((k2, v2) for k2, v2 in v.items()))
    unique_items.add(sorted_v)
unique_items = dict(unique_items)

дает для unique_items:

{1: {'description': u'testing456', 'title': u'testing123'},
 6: {'description': u'somethingelse', 'title': u'somethingelse'}}

Если items не велико (или, по крайней мере, если выходной словарь не должен быть огромным):

items = {1: {'title': u'testing123', 'description': u'testing456'},
2: {'title': u'testing123', 'description': u'testing456'},
3: {'title': u'testing123', 'description': u'testing456'},
4: {'title': u'testing123', 'description': u'testing456'},
5: {'title': u'testing123', 'description': u'testing456'},
6: {'title': u'somethingelse', 'description': u'somethingelse'}}

unique_items = {}
for k, v in items.items():
    if v not in unique_items.values():
        unique_items[k] = v

(при условии, что первый ключ в вашем примере со словарем должен быть title). Но вы не можете предсказать, какими будут ключи к этому словарю, если дубликаты существуют в items.

person xnx    schedule 12.12.2014
comment
Решение исходной проблемы - это не то, о чем я прошу. Пожалуйста, обратитесь к основной проблеме этого вопроса, которая касается причины ошибки, которую я получаю. - person Morteza R; 12.12.2014
comment
Ах - хорошо: не понимал, что вы ищете решение на основе set. Я отредактировал свой ответ, чтобы предоставить один. - person xnx; 12.12.2014

Вы пытаетесь создать набор словарей, но это невозможно, поскольку словари нельзя хэшировать (поскольку они изменяемы — их равенство может измениться при изменении/добавлении/удалении пар в словаре).

Возможно, вместо использования диктов вы можете использовать кортежи их значений для своего набора, а-ля if not set((v['description_a'], v['description_b]) for v in value.values()).issubset((v['description_a'], v['description_b]) for v in set(dic.values())): или что-то подобное?

person Mike Graham    schedule 12.12.2014
comment
Это не набор диктовок, это набор значений диктовок. Большая разница! - person Morteza R; 12.12.2014
comment
На самом деле эта часть кода работает вполне нормально. Ошибка в той части, когда я выполняю задание dic[key] = value - person Morteza R; 12.12.2014