Quais são as opções para clonar ou copiar uma lista no Python?
Durante o uso new_list = my_list
, todas as modificações nas new_list
alterações são my_list
sempre alteradas . Por que é isso?
Com new_list = my_list
, você não tem duas listas. A atribuição apenas copia a referência à lista, não a lista real, portanto, ambas new_list
e se my_list
referem à mesma lista após a atribuição.
Para realmente copiar a lista, você tem várias possibilidades:
Você pode usar o list.copy()
método builtin (disponível desde o Python 3.3):
new_list = old_list.copy()
Você pode cortá-lo:
new_list = old_list[:]
A opinião de Alex Martelli (pelo menos em 2007 ) sobre isso é que é uma sintaxe estranha e não faz sentido usá-lo sempre . ;) (Na sua opinião, o próximo é mais legível).
Você pode usar a list()
função incorporada:
new_list = list(old_list)
Você pode usar genérico copy.copy()
:
import copy
new_list = copy.copy(old_list)
Isso é um pouco mais lento do que list()
porque precisa descobrir o tipo de dados old_list
primeiro.
Se a lista contiver objetos e você também quiser copiá-los, use genérico copy.deepcopy()
:
import copy
new_list = copy.deepcopy(old_list)
Obviamente, o método mais lento e com maior necessidade de memória, mas às vezes inevitável.
Exemplo:
import copy
class Foo(object):
def __init__(self, val):
self.val = val
def __repr__(self):
return 'Foo({!r})'.format(self.val)
foo = Foo(1)
a = ['foo', foo]
b = a.copy()
c = a[:]
d = list(a)
e = copy.copy(a)
f = copy.deepcopy(a)
# edit orignal list and instance
a.append('baz')
foo.val = 5
print('original: %r\nlist.copy(): %r\nslice: %r\nlist(): %r\ncopy: %r\ndeepcopy: %r'
% (a, b, c, d, e, f))
Resultado:
original: ['foo', Foo(5), 'baz']
list.copy(): ['foo', Foo(5)]
slice: ['foo', Foo(5)]
list(): ['foo', Foo(5)]
copy: ['foo', Foo(5)]
deepcopy: ['foo', Foo(1)]
newlist = [*mylist]
também é uma possibilidade no Python 3.newlist = list(mylist)
talvez seja mais claro.Felix já forneceu uma excelente resposta, mas pensei em fazer uma comparação rápida dos vários métodos:
copy.deepcopy(old_list)
Copy()
método python puro que copia classes com deepcopyCopy()
método python puro que não copia classes (apenas dict / lists / tuples)for item in old_list: new_list.append(item)
[i for i in old_list]
(uma compreensão da lista )copy.copy(old_list)
list(old_list)
new_list = []; new_list.extend(old_list)
old_list[:]
( lista de fatias )Portanto, o mais rápido é o fatiamento de lista. Mas esteja ciente de que
copy.copy()
,list[:]
elist(list)
, ao contráriocopy.deepcopy()
ea versão python não copie quaisquer listas, dicionários e instâncias de classe na lista, por isso, se os originais mudar, eles vão mudar na lista copiado também e vice-versa.(Aqui está o script, se alguém estiver interessado ou quiser levantar algum problema :)
fonte
timeit
módulo Além disso, você não pode concluir muito de micro benchmarks arbitrários como este.[*old_list]
deve ser aproximadamente equivalente alist(old_list)
, mas, como é sintaxe, não caminhos de chamada de função geral, economiza um pouco no tempo de execução (eold_list[:]
, diferentemente do que não digita convert,[*old_list]
funciona em qualquer iterável e produz alist
).timeit
, 50m é executado em vez de 100k) veja stackoverflow.com/a/43220129/3745896[*old_list]
parece superar quase qualquer outro método. (veja minha resposta vinculada nos comentários anteriores) #Foi-me dito que o Python 3.3+ adiciona
list.copy()
método, que deve ser tão rápido quanto o fatiamento:newlist = old_list.copy()
fonte
s.copy()
cria uma cópia superficial des
(igual as[:]
).python3.8
,.copy()
é um pouco mais rápido que fatiar. Veja abaixo a resposta do @AaronsHall.No Python 3, uma cópia superficial pode ser feita com:
Nos Python 2 e 3, você pode obter uma cópia superficial com uma fatia completa do original:
Explicação
Existem duas maneiras semânticas de copiar uma lista. Uma cópia superficial cria uma nova lista dos mesmos objetos, uma cópia profunda cria uma nova lista contendo novos objetos equivalentes.
Cópia de lista rasa
Uma cópia superficial apenas copia a própria lista, que é um contêiner de referências aos objetos na lista. Se os objetos contidos em si forem mutáveis e um for alterado, a alteração será refletida nas duas listas.
Existem diferentes maneiras de fazer isso no Python 2 e 3. As formas do Python 2 também funcionarão no Python 3.
Python 2
No Python 2, a maneira idiomática de fazer uma cópia superficial de uma lista é com uma fatia completa do original:
Você também pode realizar a mesma coisa passando a lista pelo construtor da lista,
mas usar o construtor é menos eficiente:
Python 3
No Python 3, as listas obtêm o
list.copy
método:No Python 3.5:
Fazer outro ponteiro não faz uma cópia
my_list
é apenas um nome que aponta para a lista real na memória. Quando você diznew_list = my_list
que não está fazendo uma cópia, está apenas adicionando outro nome que aponta para a lista original na memória. Podemos ter problemas semelhantes quando fazemos cópias de listas.A lista é apenas uma matriz de ponteiros para o conteúdo; portanto, uma cópia superficial apenas copia os ponteiros e, portanto, você tem duas listas diferentes, mas elas têm o mesmo conteúdo. Para fazer cópias do conteúdo, você precisa de uma cópia profunda.
Cópias profundas
Para fazer uma cópia profunda de uma lista, em Python 2 ou 3, use
deepcopy
nocopy
módulo :Para demonstrar como isso nos permite criar novas sub-listas:
E, portanto, vemos que a lista profunda copiada é uma lista totalmente diferente da original. Você pode rolar sua própria função - mas não. É provável que você crie bugs que de outra forma não teria usando a função deepcopy da biblioteca padrão.
Não use
eval
Você pode ver isso usado como uma maneira de fazer uma cópia em profundidade, mas não faça isso:
No Python 2.7 de 64 bits:
no Python 3.5 de 64 bits:
fonte
list_copy=[]
for item in list: list_copy.append(copy(item))
e é muito mais rápido.Já existem muitas respostas que explicam como fazer uma cópia adequada, mas nenhuma delas diz por que a sua 'cópia' original falhou.
Python não armazena valores em variáveis; liga nomes a objetos. Sua tarefa original pegou o objeto referido
my_list
e o vinculounew_list
também. Não importa qual nome você use, ainda há apenas uma lista; portanto, as alterações feitas ao se referir a elemy_list
permanecerão quando se referir a ele comonew_list
. Cada uma das outras respostas a esta pergunta fornece maneiras diferentes de criar um novo objeto ao qual se vincular.new_list
.Cada elemento de uma lista age como um nome, pois cada elemento se liga não exclusivamente a um objeto. Uma cópia superficial cria uma nova lista cujos elementos se ligam aos mesmos objetos de antes.
Para levar sua lista a copiar um passo adiante, copie cada objeto a que sua lista se refere e vincule essas cópias de elemento a uma nova lista.
Essa ainda não é uma cópia detalhada, porque cada elemento de uma lista pode se referir a outros objetos, assim como a lista está vinculada a seus elementos. Para copiar recursivamente todos os elementos da lista e, em seguida, o outro objeto referido por cada elemento, e assim por diante: execute uma cópia profunda.
Consulte a documentação para obter mais informações sobre os casos de canto na cópia.
fonte
Usar
thing[:]
fonte
Vamos começar do começo e explorar esta questão.
Então, vamos supor que você tenha duas listas:
E temos que copiar as duas listas, agora começando na primeira lista:
Então, primeiro vamos tentar definindo a variável
copy
na nossa lista originallist_1
:Agora, se você está pensando que copiou a lista_1, está enganado. A
id
função pode nos mostrar se duas variáveis podem apontar para o mesmo objeto. Vamos tentar isso:A saída é:
Ambas as variáveis são exatamente o mesmo argumento. Você está surpreso?
Portanto, como sabemos que o python não armazena nada em uma variável, as variáveis estão apenas fazendo referência ao objeto e o objeto armazena o valor. Aqui objeto é um,
list
mas criamos duas referências a esse mesmo objeto por dois nomes de variáveis diferentes. Isso significa que ambas as variáveis estão apontando para o mesmo objeto, apenas com nomes diferentes.Quando você faz
copy=list_1
, está realmente fazendo:Aqui, na lista de imagens_1 e cópia, existem dois nomes de variáveis, mas o objeto é o mesmo para ambas as variáveis, o que é
list
Portanto, se você tentar modificar a lista copiada, ela também modificará a lista original porque a lista é apenas uma lá, você modificará essa lista, independentemente da lista copiada ou da lista original:
resultado:
Por isso, modificou a lista original:
Agora vamos passar para um método pitônico para copiar listas.
Este método corrige o primeiro problema que tivemos:
Então, como podemos ver nossa lista de ambos com um ID diferente e isso significa que ambas as variáveis estão apontando para objetos diferentes. Então, o que realmente está acontecendo aqui é:
Agora vamos tentar modificar a lista e ver se ainda enfrentamos o problema anterior:
A saída é:
Como você pode ver, apenas modificou a lista copiada. Isso significa que funcionou.
Você acha que terminamos? Não. Vamos tentar copiar nossa lista aninhada.
list_2
deve fazer referência a outro objeto que é cópialist_2
. Vamos checar:Nós obtemos a saída:
Agora podemos assumir que ambas as listas estão apontando objetos diferentes, então agora vamos tentar modificá-lo e ver se está dando o que queremos:
Isso nos dá a saída:
Isso pode parecer um pouco confuso, porque o mesmo método que usamos anteriormente funcionou. Vamos tentar entender isso.
Quando você faz:
Você está apenas copiando a lista externa, não a lista interna. Podemos usar a
id
função mais uma vez para verificar isso.A saída é:
Quando o fazemos
copy_2=list_2[:]
, isso acontece:Ele cria a cópia da lista, mas apenas a cópia da lista externa, não a cópia da lista aninhada, a lista aninhada é a mesma para as duas variáveis; portanto, se você tentar modificar a lista aninhada, também modificará a lista original, pois o objeto da lista aninhada é o mesmo para ambas as listas.
Qual é a solução? A solução é a
deepcopy
função.Vamos verificar isso:
Ambas as listas externas têm IDs diferentes, vamos tentar isso nas listas internas aninhadas.
A saída é:
Como você pode ver, os dois IDs são diferentes, o que significa que podemos assumir que ambas as listas aninhadas estão apontando objetos diferentes agora.
Isso significa que quando você faz o
deep=deepcopy(list_2)
que realmente acontece:Ambas as listas aninhadas estão apontando objetos diferentes e agora têm cópia separada da lista aninhada.
Agora vamos tentar modificar a lista aninhada e ver se ela resolveu o problema anterior ou não:
Emite:
Como você pode ver, não modificou a lista aninhada original, apenas modificou a lista copiada.
fonte
O idioma do Python para fazer isso é
newList = oldList[:]
fonte
Horário do Python 3.6
Aqui estão os resultados do tempo usando o Python 3.6.8. Lembre-se de que esses tempos são relativos um ao outro, não absolutos.
Eu continuei fazendo apenas cópias rasas e também adicionei alguns novos métodos que não eram possíveis no Python2, como
list.copy()
(o equivalente à fatia do Python3 ) e duas formas de descompactar a lista (*new_list, = list
enew_list = [*list]
):Podemos ver que o vencedor do Python2 ainda se sai bem, mas não supera
list.copy()
muito o Python3 , principalmente considerando a legibilidade superior deste último.O azarão é o método de desempacotamento e reembalagem (
b = [*a]
), que é ~ 25% mais rápido que o fatiamento bruto e mais do que o dobro do outro método de desempacotamento (*b, = a
).b = a * 1
também faz surpreendentemente bem.Observe que esses métodos não produzem resultados equivalentes para nenhuma entrada que não seja listas. Todos eles trabalham para objetos sliceable, alguns funcionam para qualquer iterável, mas
copy.copy()
funcionam apenas para objetos Python mais gerais.Aqui está o código de teste para as partes interessadas ( modelo daqui ):
fonte
b=[*a]
- a única maneira óbvia de fazê-lo;).Todos os outros colaboradores deram ótimas respostas, que funcionam quando você tem uma lista de dimensão única (nivelada), no entanto, dos métodos mencionados até agora, apenas
copy.deepcopy()
funcionam para clonar / copiar uma lista e não apontar para oslist
objetos aninhados quando você está trabalhando com listas aninhadas multidimensionais (lista de listas). Embora Felix Kling se refira a isso em sua resposta, há um pouco mais sobre o problema e possivelmente uma solução alternativa usando os built-ins que podem ser uma alternativa mais rápidadeepcopy
.Enquanto
new_list = old_list[:]
,copy.copy(old_list)'
e para Py3kold_list.copy()
trabalho para listas individuais nivelado, eles reverter para apontando para oslist
objetos aninhados dentro doold_list
e donew_list
, e as mudanças a um doslist
objetos são perpetuadas no outro.Edit: Novas informações trazidas à luz
Como outros já declararam, há problemas significativos de desempenho usando o
copy
módulo ecopy.deepcopy
para listas multidimensionais .fonte
repr()
seja suficiente para recriar o objeto. Além disso,eval()
é uma ferramenta de último recurso; veja Eval é realmente perigoso pelo veterano do SO Ned Batchelder para obter detalhes. Então, quando você defendem o usoeval()
que você realmente deve mencionar que ele pode ser perigoso.eval()
função em Python em geral é um risco. Não é tanto se você usa ou não a função no código, mas é uma falha de segurança no Python por si só. Meu exemplo não está a usá-lo com uma função que recebe entrada deinput()
,sys.agrv
ou mesmo um arquivo de texto. É mais parecido com a inicialização de uma lista multidimensional em branco uma vez e apenas com uma maneira de copiá-la em um loop, em vez de reinicializar a cada iteração do loop.new_list = eval(repr(old_list))
, portanto, além de ser uma má idéia, provavelmente também é muito lento para trabalhar.Surpreende-me que isso ainda não tenha sido mencionado, por uma questão de completude ...
Você pode descompactar a lista com o "operador de splat":,
*
que também copiará elementos da sua lista.A desvantagem óbvia desse método é que ele está disponível apenas no Python 3.5+.
Em termos de tempo, porém, isso parece ter um desempenho melhor do que outros métodos comuns.
fonte
old_list
enew_list
existem duas listas diferentes, a edição de uma não mudará a outra (a menos que você esteja mutando diretamente os elementos em si (como a lista de lista), nenhum desses métodos é uma cópia profunda).Uma abordagem muito simples, independente da versão python, estava ausente nas respostas já fornecidas, que você pode usar na maioria das vezes (pelo menos eu faço):
No entanto, se my_list contiver outros contêineres (por exemplo, listas aninhadas), você deverá usar cópia em profundidade, conforme sugerido nas respostas acima da biblioteca de cópias. Por exemplo:
. Bônus : Se você não deseja copiar elementos, use (também conhecido como cópia superficial):
Vamos entender a diferença entre a solução 1 e a solução 2
Como você pode ver, a solução nº 1 funcionou perfeitamente quando não estávamos usando as listas aninhadas. Vamos verificar o que acontecerá quando aplicarmos a solução 1 às listas aninhadas.
fonte
Observe que existem alguns casos em que se você definiu sua própria classe personalizada e deseja manter os atributos, deve usar
copy.copy()
oucopy.deepcopy()
não as alternativas, por exemplo, no Python 3:Saídas:
fonte
new_list = my_list
Tente entender isso. Digamos que my_list esteja na memória da pilha no local X, ou seja, my_list está apontando para o X. Agora, atribuindonew_list = my_list
você está Letting new_list apontando para o X. Isso é conhecido como cópia superficial.Agora, se você atribuir,
new_list = my_list[:]
você está simplesmente copiando cada objeto da minha lista para a nova lista. Isso é conhecido como cópia profunda.A outra maneira de fazer isso é:
new_list = list(old_list)
import copy new_list = copy.deepcopy(old_list)
fonte
Eu queria postar algo um pouco diferente do que algumas das outras respostas. Embora essa provavelmente não seja a opção mais compreensível ou mais rápida, ela fornece uma visão interna de como funciona a cópia em profundidade, além de ser outra opção alternativa para cópia em profundidade. Realmente não importa se minha função possui bugs, pois o objetivo é mostrar uma maneira de copiar objetos como as respostas da pergunta, mas também usá-lo como um ponto para explicar como a cópia profunda funciona em seu núcleo.
No centro de qualquer função de cópia profunda, há uma maneira de fazer uma cópia superficial. Quão? Simples. Qualquer função de cópia profunda apenas duplica os contêineres de objetos imutáveis. Ao copiar em profundidade uma lista aninhada, você está duplicando apenas as listas externas, não os objetos mutáveis dentro das listas. Você está apenas duplicando os contêineres. O mesmo vale para as aulas também. Quando você copia uma classe em profundidade, copia todos os seus atributos mutáveis. Então como? Como você só precisa copiar os contêineres, como listas, dictos, tuplas, iteradores, classes e instâncias de classe?
É simples. Um objeto mutável não pode realmente ser duplicado. Ele nunca pode ser alterado, portanto, é apenas um valor único. Isso significa que você nunca precisa duplicar strings, números, bools ou qualquer um deles. Mas como você duplicaria os contêineres? Simples. Você apenas inicializa um novo contêiner com todos os valores. A cópia em profundidade depende da recursão. Duplica todos os contêineres, mesmo aqueles com contêineres dentro deles, até que nenhum contêiner seja deixado. Um contêiner é um objeto imutável.
Depois que você souber disso, duplicar completamente um objeto sem nenhuma referência é muito fácil. Aqui está uma função para copiar profundamente tipos de dados básicos (não funcionaria para classes personalizadas, mas você sempre pode adicioná-lo)
A copia profunda interna do Python é baseada nesse exemplo. A única diferença é que ele suporta outros tipos e também suporta classes de usuários duplicando os atributos em uma nova classe duplicada e também bloqueia a recursão infinita com uma referência a um objeto que já é visto usando uma lista ou dicionário de notas. E é isso mesmo para fazer cópias profundas. No fundo, fazer uma cópia profunda é apenas fazer cópias rasas. Espero que esta resposta acrescente algo à pergunta.
EXEMPLOS
Digamos que você tenha esta lista: [1, 2, 3] . Os números imutáveis não podem ser duplicados, mas a outra camada pode. Você pode duplicar usando uma compreensão de lista: [x para x em [1, 2, 3]
Agora, imagine que você tenha esta lista: [[1, 2], [3, 4], [5, 6]] . Desta vez, você deseja criar uma função, que usa recursão para copiar em profundidade todas as camadas da lista. Em vez da compreensão da lista anterior:
Ele usa um novo para listas:
E deepcopy_list fica assim:
Agora você tem uma função que pode fazer uma cópia profunda de qualquer lista de strs, bools, floast, ints e até listas para infinitas camadas usando recursão. E aí está, cópia em profundidade.
TLDR : o Deepcopy usa recursão para duplicar objetos e simplesmente retorna os mesmos objetos imutáveis de antes, pois os objetos imutáveis não podem ser duplicados. No entanto, copia em profundidade as camadas mais internas dos objetos mutáveis até atingir a camada mutável mais externa de um objeto.
fonte
Uma pequena perspectiva prática para examinar a memória através de id e gc.
fonte
Lembre-se disso no Python quando você faz:
Lista2 não está armazenando a lista real, mas uma referência à lista1. Portanto, quando você faz qualquer coisa na lista1, a lista2 também muda. use o módulo de cópia (não padrão, faça o download no pip) para fazer uma cópia original da lista (
copy.copy()
para listas simples,copy.deepcopy()
para aninhadas). Isso faz uma cópia que não muda com a primeira lista.fonte
A opção deepcopy é o único método que funciona para mim:
leva à saída de:
fonte
Isso ocorre porque a linha
new_list = my_list
atribui uma nova referência à variávelmy_list
quenew_list
é semelhante aoC
código fornecido abaixo,Você deve usar o módulo de cópia para criar uma nova lista
fonte