2011-02-01 5 views
3

Насколько я люблю Python, справочные и глубококопийные вещи иногда меня вызывают.Deepcopy на вложенных ссылочных списках, созданных с помощью умножения списков, не работает

Почему DeepCopy здесь не работает:

>>> import copy 
>>> a = 2*[2*[0]] 
>>> a 
[[0, 0], [0, 0]] 
>>> b = copy.deepcopy(a) 
>>> b[0][0] = 1 
>>> b 
[[1, 0], [1, 0]]  #should be: [[1, 0], [0, 1]] 
>>> 

Я использую Numpy массив как workarround, который мне нужно позже в любом случае. Но я действительно надеялся, что, если бы я использовал глубокую копию, мне больше не пришлось бы преследовать какие-либо непреднамеренные ссылки. Есть ли больше ловушек, где он не работает?

ответ

10

Это не работает, потому что вы создаете массив с двумя ссылками на тот же массив.

Альтернативный подход:

[[0]*2 for i in range(2)] 

Или более явно:

[[0 for j in range(2)] for i in range(2)] 

Это работает, потому что он создает новый массив на каждой итерации.

Есть ли больше ловушек, где он не работает?

Каждый раз, когда у вас есть массив, содержащий ссылки, вы должны быть осторожны. Например, [Foo()] * 2 - это не то же самое, что и [Foo() for i in range(2)]. В первом случае строится только один объект, и массив содержит две ссылки на него. Во втором случае построены два отдельных объекта.

+1

+1, как это сделать правильно. –

+0

Спасибо за быстрые ответы. Я ценю ваше элегантное решение. Тем не менее, мне кажется, что руководство по глубокому копированию вводит в заблуждение («Глубокая копия создает новый составной объект, а затем рекурсивно вставляет * копии * в него объектов, найденных в оригинале».). Было бы неплохо, если бы функция выполняла то, что я ожидал бы от deepcopy: избавиться от всех двойных ссылок. – Gonzo

7

Он работает точно так, как вы ожидали.

а = 2 * [2 * [0]]

Когда вы умножаете [[0,0]] с 2 *, оба элемента нового списка будет указывать на тот же [0,0] список. a[0] и a[1] - это тот же список, потому что ссылка копируется, а не данные (что было бы невозможно). Изменение первого элемента одного из них изменяет первый элемент другого.

copy.deepcopy правильно копирует список, сохраняя уникальные объекты.

+0

+1 за то, почему он не работает. –