Python: найдите разницу между двумя словарями, содержащими списки

У меня есть два словаря, которые имеют следующую структуру:

a = {'joe': [24,32,422], 'bob': [1,42,32,24], 'jack':[0,3,222]}
b = {'joe': [24], 'bob': [1,42,32]}

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

{'joe': [32,422], 'bob': [24], 'jack':[0,3,222]}

Я знаю, что я мог бы сделать это с беспорядочным циклом, но я хотел бы знать, как я могу достичь этого в чистом, питоническом стиле?

Я попытался: a.items() - b.items()

но я получаю следующую ошибку: unsupported operand type(s) for -: 'dict_values' and 'dict_values'

Спасибо за помощь

Ответ 1

Предполагая, что в любом из ваших списков не будет повторяющихся записей, вы можете делать то, что хотите, с set, но не со списками:

>>> a = {'joe': [24,32,422], 'bob': [1,42,32,24], 'jack':[0,3,222]}
>>> b = {'joe': [24], 'bob': [1,42,32]}
>>> {key: list(set(a[key])- set(b.get(key,[]))) for key in a}
{'joe': [32, 422], 'bob': [24], 'jack': [0, 3, 222]}

Обратите внимание на две вещи:

  • Я конвертирую набор обратно в список, когда я устанавливаю его как значение
  • Я использую b.get вместо b[key] для обработки, если ключ не существует в b, но в a

EDIT - использование цикла for:

Я понял, что понимание может быть не таким понятным, поэтому это эквивалентный бит кода, использующий цикл for:

>>> c = {}
>>> for key in a:
    c[key] = list(set(a[key]) - set(b.get(key,[])))


>>> c
{'joe': [32, 422], 'bob': [24], 'jack': [0, 3, 222]}

РЕДАКТИРОВАТЬ - потерять второй набор:

Как упоминал Падрейк Каннингем в комментариях (как он часто это делает, благослови его душу), вы можете использовать set.difference, чтобы избежать явного включения второго списка в набор:

>>> c = {}
>>> for key in a:
    c[key] = list(set(a[key]).difference(b.get(key,[])))


>>> c
{'joe': [32, 422], 'bob': [24], 'jack': [0, 3, 222]}

или со списком:

>>> {key: list(set(a[key]).difference(b.get(key,[]))) for key in a}
{'joe': [32, 422], 'bob': [24], 'jack': [0, 3, 222]}

или если вы хотите рассматривать set.difference как метод класса вместо метода экземпляра:

>>> {key: list(set.difference(set(a[key]),b.get(key,[]))) for key in a}
{'joe': [32, 422], 'bob': [24], 'jack': [0, 3, 222]}

Хотя я нахожу это немного неудобным, и мне это не очень нравится.

Ответ 2

Вам нужно использовать наборы:

diff = {}
for key in a:
    diff[key] = list(set(a[key]) - set(b.get(key, [])))
print diff

Ответ 3

Другой способ - использовать встроенный метод filter:

>>> a = {'joe': [24,32,422], 'bob': [1,42,32,24], 'jack':[0,3,222]}
>>> b = {'joe': [24], 'bob': [1,42,32]}
>>> {key:filter(lambda s: s not in b.get(key,[]), a[key]) for key in a}
{'bob': [24], 'joe': [32, 422], 'jack': [0, 3, 222]}

Согласно Padraic Cunningham комментариям:

В Python 3, filter возвращает generator, поэтому вам нужно будет преобразовать его в список, таким образом:

{key:list(filter(lambda s: s not in b.get(key,[]), a[key])) for key in a}