Почему изменяется значение в списке 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 шт):

Автор решения: strawdog

Вы намудрили с вложенными циклами, и, кроме того, забыли, что добавляя список в другой список через 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]]
→ Ссылка
Автор решения: CrazyElf

Про ссылки и копии уже в других ответах написали (да и много было других вопросов-ответов на СО по этой теме).

Добавлю только, что в промышленном масштабе такие вещи проще и быстрее делать через 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).

→ Ссылка
Автор решения: Vitalizzare

Поддерживаю ответ 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]
→ Ссылка