Вопрос:

Как сделать полностью неразделенную копию сложного списка? (Глубокая копия не достаточно)

python list copy

10350 просмотра

5 ответа

37873 Репутация автора

Посмотрите на этот код 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, пожалуйста, не стесняйтесь изменять вопрос или теги, чтобы лучше выразить этот запрос.)

Автор: Ashwin Nanjappa Источник Размещён: 21.10.2009 02:38

Ответы (5)


5 плюса

50427 Репутация автора

Используйте [:]:

>>> 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.

Автор: Stephan202 Размещён: 21.10.2009 02:41

5 плюса

22577 Репутация автора

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

Автор: Pesto Размещён: 21.10.2009 02:42

8 плюса

671123 Репутация автора

Когда вам нужна копия, вы явно делаете копию - загадочная [:]форма «нарезать все» идиоматична, но мой фаворит - гораздо более читаемый подход явного вызова 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)аккуратно скопировало бы любую топологию c, включая множественные ссылки на одни и те же подсписки! :-)

Автор: Alex Martelli Размещён: 21.10.2009 02:47

1 плюс

28757 Репутация автора

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

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]]]
Автор: Michael Dillon Размещён: 21.10.2009 02:47

8 плюса

95403 Репутация автора

Решение

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

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 или пользовательские классы), вам может потребоваться изменить это.

Автор: Brian Размещён: 21.10.2009 03:49
Вопросы из категории :
32x32