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

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
Alan 2 Май 2019 в 07:46

3 ответа

Лучший ответ

Здесь 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)
        ...
4
Miguel Garcia 2 Май 2019 в 04:53

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

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]}]

< Сильный > Объяснение / Разъяснение :

Это понимание списка делает то, что нужно OP. Мы начинаем с перебора каждого словаря в нашем списке словарей 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
kmario23 3 Май 2019 в 07:33

Ответы выше отличные. Благодарность! Здесь я просто объясняю причину ошибки простым способом. Я добавил два 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.

0
Juliecodestack 3 Май 2019 в 00:22