Как распаковать ключи со списком значений в несколько словарей в список без перезаписи? - PullRequest
2 голосов
/ 02 мая 2019

У меня есть список словарей:

data = [
    {'name': 'foo', 'scores': [2]},
    {'name': 'bar', 'scores': [4, 9, 3]},
    {'name': 'baz', 'scores': [6, 1]}
]

Я хочу создать новый список, в котором каждый отдельный человек score будет выделен следующим образом:

list = [
    {'name': 'foo', 'scores': [2], 'score': 2},
    {'name': 'bar', 'scores': [4, 9, 3], 'score': 4},
    {'name': 'bar', 'scores': [4, 9, 3], 'score': 9},
    {'name': 'bar', 'scores': [4, 9, 3], 'score': 3},
    {'name': 'baz', 'scores': [6, 1], 'score': 6},
    {'name': 'baz', 'scores': [6, 1], 'score': 1}
]

Тогда я могупереберите все row и score, чтобы создать новый словарь:

for row in data:
    scores = row['scores']  # list of values
    for score in scores:
        new_row = row
        new_row['score'] = score
        print(new_row)

, который дает мне именно то, что я хочу:

{'name': 'foo', 'scores': [2], 'score': 2}
{'name': 'bar', 'scores': [4, 9, 3], 'score': 4}
{'name': 'bar', 'scores': [4, 9, 3], 'score': 9}
{'name': 'bar', 'scores': [4, 9, 3], 'score': 3}
{'name': 'baz', 'scores': [6, 1], 'score': 6}
{'name': 'baz', 'scores': [6, 1], 'score': 1}

Однако явозникли проблемы при добавлении этих словарей в список.Когда я использую функцию append() для добавления каждого словаря в новый список:

list = []

for row in data:
    scores = row['scores']  # list of values
    for score in scores:
        new_row = row
        new_row['score'] = score
        list.append(new_row)

    print(list)

Кажется, что перезаписывает некоторые из предыдущих пунктов:

[
{'name': 'foo', 'scores': [2], 'score': 2},
{'name': 'bar', 'scores': [4, 9, 3], 'score': 3},
{'name': 'bar', 'scores': [4, 9, 3], 'score': 3},
{'name': 'bar', 'scores': [4, 9, 3], 'score': 3},
{'name': 'baz', 'scores': [6, 1], 'score': 1},
{'name': 'baz', 'scores': [6, 1], 'score': 1}
]

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

Ответы [ 3 ]

4 голосов
/ 02 мая 2019

Как насчет простого понимания списка, чтобы достичь всего этого за один шаг:

In [269]: [{**d, **{'score': v}} for d in data for v in d['scores']]
Out[269]: 
[{'name': 'foo', 'score': 2, 'scores': [2]},
 {'name': 'bar', 'score': 4, 'scores': [4, 9, 3]},
 {'name': 'bar', 'score': 9, 'scores': [4, 9, 3]},
 {'name': 'bar', 'score': 3, 'scores': [4, 9, 3]},
 {'name': 'baz', 'score': 6, 'scores': [6, 1]},
 {'name': 'baz', 'score': 1, 'scores': [6, 1]}]

Объяснение / уточнение :

Это понимание спискаделает то, что ОП, наконец, нужно.Мы начинаем с перебора каждого словаря в нашем списке словарей data и для каждого значения v в текущем словаре scores с помощью этого вложенного цикла for,

for d in data for v in d['scores']  # order goes from left to right

мы добавляем ключ score и значение v путем распаковки, а затем мы также распаковываем текущий словарь, поскольку OP также нуждается в этом.В конце мы объединяем оба из них, используя {**d, **{'score': v}}, и это то, что нам нужно для достижения.

Объединение выполняется с использованием { } или dict(), поскольку мы распаковываем ключи и значения из обоих dи {'score': v};Таким образом, альтернатива:

In [3]: [dict(**d, **{'score': v}) for d in data for v in d['scores']]
Out[3]: 
[{'name': 'foo', 'score': 2, 'scores': [2]},
 {'name': 'bar', 'score': 4, 'scores': [4, 9, 3]},
 {'name': 'bar', 'score': 9, 'scores': [4, 9, 3]},
 {'name': 'bar', 'score': 3, 'scores': [4, 9, 3]},
 {'name': 'baz', 'score': 6, 'scores': [6, 1]},
 {'name': 'baz', 'score': 1, 'scores': [6, 1]}]

Для получения дополнительной информации о примерах распаковки словаря, пожалуйста, обратитесь к peps / pep-0448 /

4 голосов
/ 02 мая 2019

Здесь new_row всегда ссылается на текущий row объект, то есть одинаково для каждой оценки в этом объекте строки.Вам нужно создать новый объект, копирующий текущую строку.Используйте deepcopy из пакета copy.

from copy import deepcopy
for row in data:
    scores = row['scores']  # list of values
    for score in scores:
        new_row = deepcopy(row)
        ...
0 голосов
/ 03 мая 2019

Ответы выше отличные.Спасибо!Здесь я просто объясняю причину ошибки простым способом.Я добавил два print ():

for score in scores:
        print(row)
        new_row = row
        new_row['score'] = score
        list.append(new_row)
        print(list)

часть результатов:

......
{'name': 'bar', 'scores': [4, 9, 3]}
[{'name': 'foo', 'scores': [2], 'score': 2}, {'name': 'bar', 'scores': [4, 9, 3], 'score': 4}]
{'name': 'bar', 'scores': [4, 9, 3], 'score': 4}
[{'name': 'foo', 'scores': [2], 'score': 2}, {'name': 'bar', 'scores': [4, 9, 3], 'score': 9}, {'name': 'bar', 'scores': [4, 9, 3], 'score': 9}]
{'name': 'bar', 'scores': [4, 9, 3], 'score': 9}
[{'name': 'foo', 'scores': [2], 'score': 2}, {'name': 'bar', 'scores': [4, 9, 3], 'score': 3}, {'name': 'bar', 'scores': [4, 9, 3], 'score': 3}, {'name': 'bar', 'scores': [4, 9, 3], 'score': 3}]
......

Так что теперь мы можем видеть, когда new_row = row, они относятся ктот же объект.Когда new_row изменяется, строка также изменяется.Результат списка является результатом последнего цикла для каждого scores.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...