Почему изменяется значение в списке Python
Есть список num3, состоящий из вложенных списков, и список n, состоящий из чисел.
Я беру первый подсписок из num3, добавляю его в список values_list_3. Затем беру последний элемент из этого списка, т.е. по факту то, что только что добавилось, и в конец этого подсписка хочу добавить первое число из списка n.
В результате, после обхода всех элементов списка n в списке values_list_3 должно быть следующее:
values_list_3 = [[20, 15, 3, 5, 3], [20, 15, 3, 5, 5],
[20, 15, 3, 5, 6], [20, 15, 3, 5, 9],
[20, 15, 3, 5, 21], [20, 15, 3, 5, 18]]
Но это не работает как хотелось бы. О возникших проблемах написал на скрине, там же окно с отладчиком после первой итерации вложенного цикла for. Прошу подсказать в чем проблема. В особенности интересует почему при итерации вложенного цикла for изменяются значения самого первого списка num3
num3 = [[20, 15, 3, 5], [...], ...]
n = [3, 5, 6, 9, 21, 18]
values_list_3 = []
for i in num3:
for j in range(len(n)):
values_list_3.append(i)
values_list_3[-1].append(n[j])
Ответы (3 шт):
Вы намудрили с вложенными циклами, и, кроме того, забыли, что добавляя список в другой список через append вы на самом деле добавляете ссылку на оригинальный список, а не создаете его копию. Поэтому, все что вы делаете с добавленным подсписком, вы на самом деле делаете с оригинальным списком.
По вложенным циклам - вы не просто проходитесь по подспискам оригинального списка, вы проходитесь по ним len(n) раз, благодаря ненужному второму циклу.
быстрое решение может быть таким:
num_3 = [[20, 15, 3, 5], [20, 15, 3, 5],
[20, 15, 3, 5], [20, 15, 3, 5],
[20, 15, 3, 5], [20, 15, 3, 5]]
n = [3,5,6,9,21,18]
values_list_3 = []
for i in range(len(num_3)): # один цикл
values_list_3.append(num_3[i][:]) # [:] - это 'грязный хак' - получение
# копии списка, а не ссылки на оригинал
# советую почитать про метод copy()
values_list_3[-1].append(n[i])
print(values_list_3)
print(num_3)
[[20, 15, 3, 5, 3], [20, 15, 3, 5, 5], [20, 15, 3, 5, 6], [20, 15, 3, 5, 9], [20, 15, 3, 5, 21], [20, 15, 3, 5, 18]]
[[20, 15, 3, 5], [20, 15, 3, 5], [20, 15, 3, 5], [20, 15, 3, 5], [20, 15, 3, 5], [20, 15, 3, 5]]
Про ссылки и копии уже в других ответах написали (да и много было других вопросов-ответов на СО по этой теме).
Добавлю только, что в промышленном масштабе такие вещи проще и быстрее делать через numpy, там для любых операций с векторами и матрицами есть встроенные оптимизированные методы:
import numpy as np
num_3 = np.array([[20, 15, 3, 5], [20, 15, 3, 5],
[20, 15, 3, 5], [20, 15, 3, 5],
[20, 15, 3, 5], [20, 15, 3, 5]])
n = np.array([3,5,6,9,21,18])
result = np.hstack((num_3, n.reshape(-1,1)))
print(result)
Вывод:
[[20 15 3 5 3]
[20 15 3 5 5]
[20 15 3 5 6]
[20 15 3 5 9]
[20 15 3 5 21]
[20 15 3 5 18]]
numpy.hstack как-раз горизонтально h состыковывает stack две матрицы (а n у нас вектор, поэтому приходится ему делать reshape, чтобы превратить в матрицу шириной 1).
Поддерживаю ответ Strawdog, особенно замечание о необходимости копировать вложенные списки перед их модификацией.
От себя добавлю, что для параллельного прохода по двум и более последовательностям в Python используется встроенная функция zip:
num_3 = [[20, 15, 3, 5], [20, 15, 3, 5],
[20, 15, 3, 5], [20, 15, 3, 5],
[20, 15, 3, 5], [20, 15, 3, 5]]
n = [3,5,6,9,21,18]
values_list_3 = [[*seq, number] for seq, number in zip(num_3, n)]
Однако, из вопроса не ясно, что именно является целью: добавить в конец каждой последовательности число, расположенное по такому же индексу, или продублировать каждую последовательность, последовательно добавляя в конец каждое число.
Если вы ставите перед собой вторую задачу, то ваши действия верны с точностью до необходимости копировать подпоследовательность перед её изменением:
num3 = [[1, 2], [3, 4]] # сделаем набор последовательностей
n = [10, 11, 12] # и набор чисел разное длины
# возможный ожидаемый ответ
expected = [[1, 2, 10], [1, 2, 11], [1, 2, 12],
[3, 4, 10], [3, 4, 11], [3, 4, 12]]
values_list_3 = []
for seq in num3:
for number in n:
values_list_3.append(seq[:]) # добавляем копию подпоследовательности
values_list_3[-1].append(number)
# убеждаемся, что полученный результат равен ожидаемому
assert values_list_3 == expected
Или чуть более сжато:
values_list_3 = [[*seq, number] for seq in num3 for number in n]
