Как сделать полностью неразделенную копию сложного списка? (Глубокая копия не достаточно) - PullRequest
6 голосов
/ 21 октября 2009

Посмотрите на этот код Python:

a = [1, 2, 3]
b = [4, 5, 6]
c = [[a, b], [b, a]] # [[[1, 2, 3], [4, 5, 6]], [[4, 5, 6], [1, 2, 3]]]
c[0][0].append(99)   # [[[1, 2, 3, 99], [4, 5, 6]], [[4, 5, 6], [1, 2, 3, 99]]]

Обратите внимание, что изменение одного элемента c изменяет это везде. То есть, если 99 добавлено к c[0][0], оно также добавлено к c[1][1]. Я предполагаю, что это потому, что Python ловко ссылается на тот же объект для c[0][0] и c[1][1]. (То есть их id () такой же.)

Вопрос: Есть ли что-нибудь, что можно сделать с c, чтобы его элементы списка можно было безопасно локально изменить? Выше приведен только пример, моя реальная проблема состоит в том, что список гораздо сложнее, но с аналогичной проблемой.

(Извините за плохо сформулированный вопрос выше. Гуру Python, пожалуйста, не стесняйтесь изменять вопрос или теги, чтобы лучше выразить этот запрос.)

Ответы [ 5 ]

8 голосов
/ 21 октября 2009

Чтобы преобразовать существующий список списков в тот, где ничего не передано, вы можете рекурсивно скопировать список.

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

def unshared_copy(inList):
    if isinstance(inList, list):
        return list( map(unshared_copy, inList) )
    return inList

alist = unshared_copy(your_function_returning_lists())

Обратите внимание, что предполагается, что данные возвращаются в виде списка списков (произвольно вложенных). Если контейнеры относятся к разным типам (например, массивам numpy, dict или пользовательским классам), вам может потребоваться изменить это.

8 голосов
/ 21 октября 2009

Когда вам нужна копия, вы явно делаете копию - загадочная [:] форма «нарезать все» идиоматична, но мой любимый - гораздо более читаемый подход явного вызова list.

Если c создан неверным образом (со ссылками вместо мелких копий на списки, которые вы хотите изменить самостоятельно), лучше всего будет исправить способ, которым он построен (зачем создавать его неправильно, а затем трудиться исправить это ?!), но если это не под вашим контролем, то можно отменить повреждение - просто зацикливайтесь на c (рекурсивно, если необходимо) с индексом, переназначая соответствующие подсписки их копиям. Например, если вы точно знаете, что структура c является двухуровневой, как вы указали, вы можете сохранить себя без рекурсии:

def fixthewronglymadelist(c):
  for topsublist in c:
    for i, L in enumerate(topsublist):
      topsublist[i] = list(L)

Несмотря на то, что предлагают другие ответы, copy.deepcopy было бы трудно склониться к этой особой цели, если бы все, что вам дали, было неправильно сделано c: выполнение только copy.deepcopy(c) аккуратно скопировало бы любую топологию с, включая несколько ссылок на одни и те же подсписки! : -)

5 голосов
/ 21 октября 2009

В зависимости от вашей ситуации вы можете работать с глубокой копией этого списка.

5 голосов
/ 21 октября 2009

Использование [:]:

>>> a = [1, 2]
>>> b = a[:]
>>> b.append(9)
>>> a
[1, 2]

Альтернативно, используйте copy или deepcopy:

>>> import copy
>>> a = [1, 2]
>>> b = copy.copy(a)
>>> b.append(9)
>>> a
[1, 2]

copy работает с объектами, отличными от списков. Для списков это имеет тот же эффект, что и a[:]. deepcopy пытается рекурсивно копировать вложенные элементы и, таким образом, является более "тщательной" операцией, чем copy.

1 голос
/ 21 октября 2009

Чтобы увидеть предложение Стефана на работе, сравните два вывода ниже:

a = [1, 2, 3]
b = [4, 5, 6]
c = [[a, b], [b, a]]
c[0][0].append(99)
print c
print "-------------------"
a = [1, 2, 3]
b = [4, 5, 6]
c = [[a[:], b[:]], [b[:], a[:]]]
c[0][0].append(99)
print c

Вывод выглядит следующим образом:

[[[1, 2, 3, 99], [4, 5, 6]], [[4, 5, 6], [1, 2, 3, 99]]]
-------------------
[[[1, 2, 3, 99], [4, 5, 6]], [[4, 5, 6], [1, 2, 3]]]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...