Коли ви пишете, [x]*3
ви отримуєте, по суті, список [x, x, x]
. Тобто список із 3 посиланнями на той самий x
. Після цього ви модифікуєте цей сингл, x
він видно через усі три посилання на нього:
x = [1] * 4
l = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
print(
f"id(l[0]): {id(l[0])}\n"
f"id(l[1]): {id(l[1])}\n"
f"id(l[2]): {id(l[2])}"
)
# id(l[0]): 140560897920048
# id(l[1]): 140560897920048
# id(l[2]): 140560897920048
x[0] = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"l: {l}")
# l: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]
Щоб виправити це, вам потрібно переконатися, що ви створюєте новий список на кожній позиції. Один із способів це зробити
[[1]*4 for _ in range(3)]
яка буде переоцінювати [1]*4
кожного разу замість того, щоб оцінювати її один раз і робити 3 посилання на 1 список.
Вам може бути цікаво, чому *
не можна робити незалежні об’єкти так, як це робить розуміння списку. Це тому, що оператор множення *
працює над об'єктами, не бачачи виразів. Коли ви використовуєте *
для множення [[1] * 4]
на 3, *
бачить лише 1-елементний список, який [[1] * 4]
оцінює, а не [[1] * 4
текст виразу. *
не має ідеї, як зробити копії цього елемента, не має ідеї, як переоцінити [[1] * 4]
, і немає ідеї, що ви навіть хочете копії, і взагалі, може навіть не бути способу копіювання елемента.
Єдиний варіант *
полягає в тому, щоб робити нові посилання на існуючий підспис, а не намагатися вносити нові списки. Все інше було б суперечливим або вимагало б значного перероблення основних мовних дизайнерських рішень.
Навпаки, розуміння списку переоцінює вираз елемента на кожній ітерації. [[1] * 4 for n in range(3)]
переоцінюється [1] * 4
кожен раз з тієї ж причини, щоразу [x**2 for x in range(3)]
переоцінюється x**2
. Кожна оцінка [1] * 4
генерує новий список, тому розуміння списку робить те, що ви хотіли.
Між іншим, [1] * 4
також не копіює елементи [1]
, але це не має значення, оскільки цілі числа незмінні. Ви не можете зробити щось на кшталт 1.value = 2
і перетворити 1 на 2.
[x]*3
зберігати 3 посилання, як[x, x, x]
це правильно, лише колиx
він змінений. Це не працює, наприкладa=[4]*3
, де післяa[0]=5
,a=[5,4,4].