Como fazer uma cópia completamente não compartilhado de uma lista complicado? (Cópia em profundidade não é suficiente)

StackOverflow https://stackoverflow.com/questions/1601269

  •  05-07-2019
  •  | 
  •  

Pergunta

Tenha um olhar para este código 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]]]

Observe como modificar um elemento da modifica c que em todos os lugares. Ou seja, se 99 é anexado ao c[0][0], também é anexado ao c[1][1]. Eu estou supondo que isto é porque Python é habilmente referindo ao mesmo objeto para c[0][0] e c[1][1]. (Essa é a sua id () é o mesmo.)

Pergunta: Existe algo que pode ser feito para c para que seus elementos de lista pode ser seguramente modificado localmente? Acima é apenas um exemplo, o meu verdadeiro problema tem uma lista muito mais complicado, mas tendo um problema semelhante.

(gurus Desculpem a pergunta mal formado acima. Python sinta-se livre para modificar a pergunta ou tags para expressar melhor essa consulta.)

Foi útil?

Solução

Para converter uma lista existente de listas para uma onde nada é compartilhado, você recursivamente poderia copiar a lista.

deepcopy não será suficiente, uma vez que irá copiar a estrutura como está, mantendo Interno referências como referências, não cópias.

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

alist = unshared_copy(your_function_returning_lists())

Note que este assume os dados são retornados como uma lista de listas (arbitrariamente aninhados). Se os recipientes são de tipos diferentes (por exemplo. Matrizes numpy, dicts, ou classes de usuários), você pode precisar alterar isso.

Outras dicas

Quando você quiser uma cópia, você faz explicitamente uma cópia - o [:] cryptical "fatia tudo" forma é idiomático, mas o meu favorito é a abordagem muito mais legível de list chamando explicitamente

.

Se c é construído de forma errada (com referências em vez de cópias rasas para as listas que você quer ser capaz de modificar de forma independente), a melhor coisa seria para corrigir a forma como é construído (por que construí-lo errado e, em seguida, o trabalho de correção isso ?!), mas se isso é fora de seu controle, é possível desfazer o dano - apenas laço em c (de forma recursiva, se necessário), com um índice, a reafectação dos sublists relevantes para suas cópias. Por exemplo, se você tem certeza de que a estrutura do c é de dois níveis como você indicar, você pode salvar-se sem recursão:

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

Apesar do que outras respostas sugerem, copy.deepcopy seria difícil de dobrar a este propósito peculiar, se tudo que você está dado é o c erroneamente feito: fazendo apenas copy.deepcopy(c) seria cuidadosamente replicar tudo o que c de topologia é, incluindo várias referências aos mesmos sublists ! : -)

Dependendo você situação, você pode querer trabalhar contra um cópia profunda deste lista.

Use [:]:

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

, alternativamente, use copy ou deepcopy :

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

copy funciona em diferentes listas de objetos. Para listas, tem o mesmo efeito que a[:]. tentativas deepcopy para recursivamente copiar elementos aninhados, e é, portanto, uma operação mais "completa", que copy.

Para ver a sugestão de Stephan no trabalho, comparar as duas saídas abaixo:

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

A saída é a seguinte:

[[[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]]]
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top