Jawaban:
Dengan new_list = my_list
, Anda sebenarnya tidak memiliki dua daftar. Tugas hanya menyalin referensi ke daftar, bukan daftar yang sebenarnya, jadi keduanya new_list
dan my_list
merujuk ke daftar yang sama setelah penugasan.
Untuk benar-benar menyalin daftar, Anda memiliki berbagai kemungkinan:
Anda dapat menggunakan list.copy()
metode builtin (tersedia sejak Python 3.3):
new_list = old_list.copy()
Anda dapat mengirisnya:
new_list = old_list[:]
Pendapat Alex Martelli (setidaknya pada tahun 2007 ) tentang ini adalah, bahwa ini adalah sintaks aneh dan tidak masuk akal untuk menggunakannya . ;) (Menurutnya, yang berikutnya lebih mudah dibaca).
Anda dapat menggunakan list()
fungsi bawaan:
new_list = list(old_list)
Anda dapat menggunakan generik copy.copy()
:
import copy
new_list = copy.copy(old_list)
Ini sedikit lebih lambat daripada list()
karena harus mencari tahu tipe data old_list
pertama.
Jika daftar berisi objek dan Anda juga ingin menyalinnya, gunakan generik copy.deepcopy()
:
import copy
new_list = copy.deepcopy(old_list)
Jelas metode yang paling lambat dan paling membutuhkan memori, tetapi kadang-kadang tidak dapat dihindari.
Contoh:
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))
Hasil:
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)]
Felix sudah memberikan jawaban yang sangat baik, tetapi saya pikir saya akan melakukan perbandingan cepat dari berbagai metode:
copy.deepcopy(old_list)
Copy()
metode menyalin kelas dengan deepcopyCopy()
metode tidak menyalin kelas (hanya perintah / daftar / tupel)for item in old_list: new_list.append(item)
[i for i in old_list]
( daftar pemahaman )copy.copy(old_list)
list(old_list)
new_list = []; new_list.extend(old_list)
old_list[:]
( daftar pengirisan )Jadi yang tercepat adalah daftar slicing. Tapi ketahuilah itu copy.copy()
, list[:]
dan list(list)
, tidak seperticopy.deepcopy()
dan versi python tidak menyalin daftar, kamus dan instance kelas dalam daftar, jadi jika aslinya berubah, mereka akan berubah dalam daftar yang disalin juga dan sebaliknya.
(Ini skripnya jika ada yang tertarik atau ingin mengangkat masalah apa pun :)
from copy import deepcopy
class old_class:
def __init__(self):
self.blah = 'blah'
class new_class(object):
def __init__(self):
self.blah = 'blah'
dignore = {str: None, unicode: None, int: None, type(None): None}
def Copy(obj, use_deepcopy=True):
t = type(obj)
if t in (list, tuple):
if t == tuple:
# Convert to a list if a tuple to
# allow assigning to when copying
is_tuple = True
obj = list(obj)
else:
# Otherwise just do a quick slice copy
obj = obj[:]
is_tuple = False
# Copy each item recursively
for x in xrange(len(obj)):
if type(obj[x]) in dignore:
continue
obj[x] = Copy(obj[x], use_deepcopy)
if is_tuple:
# Convert back into a tuple again
obj = tuple(obj)
elif t == dict:
# Use the fast shallow dict copy() method and copy any
# values which aren't immutable (like lists, dicts etc)
obj = obj.copy()
for k in obj:
if type(obj[k]) in dignore:
continue
obj[k] = Copy(obj[k], use_deepcopy)
elif t in dignore:
# Numeric or string/unicode?
# It's immutable, so ignore it!
pass
elif use_deepcopy:
obj = deepcopy(obj)
return obj
if __name__ == '__main__':
import copy
from time import time
num_times = 100000
L = [None, 'blah', 1, 543.4532,
['foo'], ('bar',), {'blah': 'blah'},
old_class(), new_class()]
t = time()
for i in xrange(num_times):
Copy(L)
print 'Custom Copy:', time()-t
t = time()
for i in xrange(num_times):
Copy(L, use_deepcopy=False)
print 'Custom Copy Only Copying Lists/Tuples/Dicts (no classes):', time()-t
t = time()
for i in xrange(num_times):
copy.copy(L)
print 'copy.copy:', time()-t
t = time()
for i in xrange(num_times):
copy.deepcopy(L)
print 'copy.deepcopy:', time()-t
t = time()
for i in xrange(num_times):
L[:]
print 'list slicing [:]:', time()-t
t = time()
for i in xrange(num_times):
list(L)
print 'list(L):', time()-t
t = time()
for i in xrange(num_times):
[i for i in L]
print 'list expression(L):', time()-t
t = time()
for i in xrange(num_times):
a = []
a.extend(L)
print 'list extend:', time()-t
t = time()
for i in xrange(num_times):
a = []
for y in L:
a.append(y)
print 'list append:', time()-t
t = time()
for i in xrange(num_times):
a = []
a.extend(i for i in L)
print 'generator expression extend:', time()-t
timeit
modul. juga, Anda tidak bisa menyimpulkan banyak dari tolok ukur mikro sewenang-wenang seperti ini.
[*old_list]
harus kira-kira setara dengan list(old_list)
, tetapi karena itu sintaks, bukan jalur panggilan fungsi umum, ini akan menghemat sedikit saat runtime (dan tidak seperti old_list[:]
, yang tidak mengetik konversi, [*old_list]
bekerja pada setiap iterable dan menghasilkan a list
).
timeit
, 50m berjalan bukannya 100k) lihat stackoverflow.com/a/43220129/3745896
[*old_list]
tampaknya mengungguli hampir semua metode lain. (lihat jawaban saya yang ditautkan dalam komentar sebelumnya)
Saya telah diberitahu bahwa Python 3.3+ menambahkanlist.copy()
metode, yang harus secepat memotong:
newlist = old_list.copy()
s.copy()
membuat salinan dangkal s
(sama seperti s[:]
).
python3.8
, .copy()
adalah sedikit lebih cepat daripada mengiris. Lihat jawaban @AaronsHall di bawah ini.
Apa opsi untuk mengkloning atau menyalin daftar dengan Python?
Dalam Python 3, salinan dangkal dapat dibuat dengan:
a_copy = a_list.copy()
Dalam Python 2 dan 3, Anda bisa mendapatkan salinan dangkal dengan potongan penuh dari aslinya:
a_copy = a_list[:]
Ada dua cara semantik untuk menyalin daftar. Salinan dangkal membuat daftar baru dari objek yang sama, salinan dalam membuat daftar baru yang berisi objek setara baru.
Salinan dangkal hanya menyalin daftar itu sendiri, yang merupakan wadah referensi ke objek dalam daftar. Jika objek yang terkandung sendiri bisa berubah dan satu diubah, perubahan akan tercermin di kedua daftar.
Ada berbagai cara untuk melakukan ini di Python 2 dan 3. Cara Python 2 juga akan bekerja di Python 3.
Dalam Python 2, cara idiomatis membuat salinan daftar yang dangkal adalah dengan sepotong yang asli:
a_copy = a_list[:]
Anda juga dapat mencapai hal yang sama dengan melewati daftar melalui konstruktor daftar,
a_copy = list(a_list)
tetapi menggunakan konstruktor kurang efisien:
>>> timeit
>>> l = range(20)
>>> min(timeit.repeat(lambda: l[:]))
0.30504298210144043
>>> min(timeit.repeat(lambda: list(l)))
0.40698814392089844
Di Python 3, daftar dapatkan list.copy
metode:
a_copy = a_list.copy()
Dalam Python 3.5:
>>> import timeit
>>> l = list(range(20))
>>> min(timeit.repeat(lambda: l[:]))
0.38448613602668047
>>> min(timeit.repeat(lambda: list(l)))
0.6309100328944623
>>> min(timeit.repeat(lambda: l.copy()))
0.38122922903858125
Menggunakan new_list = my_list kemudian memodifikasi new_list setiap kali perubahan my_list. Kenapa ini?
my_list
hanyalah nama yang menunjuk ke daftar aktual dalam memori. Ketika Anda mengatakan new_list = my_list
Anda tidak membuat salinan, Anda hanya menambahkan nama lain yang menunjuk pada daftar asli dalam memori. Kami dapat memiliki masalah serupa ketika kami membuat salinan daftar.
>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]
Daftar ini hanyalah sebuah array pointer ke konten, jadi salinan dangkal hanya menyalin pointer, dan Anda memiliki dua daftar berbeda, tetapi mereka memiliki konten yang sama. Untuk membuat salinan konten, Anda perlu salinan yang dalam.
Untuk membuat salinan daftar, dengan Python 2 atau 3, gunakan deepcopy
dalam copy
modul :
import copy
a_deep_copy = copy.deepcopy(a_list)
Untuk menunjukkan bagaimana ini memungkinkan kami membuat sub-daftar baru:
>>> import copy
>>> l
[['foo'], [], []]
>>> l_deep_copy = copy.deepcopy(l)
>>> l_deep_copy[0].pop()
'foo'
>>> l_deep_copy
[[], [], []]
>>> l
[['foo'], [], []]
Jadi kita melihat bahwa daftar yang disalin dalam adalah daftar yang sama sekali berbeda dari aslinya. Anda dapat memutar fungsi Anda sendiri - tetapi tidak. Anda kemungkinan akan membuat bug yang tidak Anda miliki dengan menggunakan fungsi deepcopy perpustakaan standar.
eval
Anda mungkin melihat ini digunakan sebagai cara untuk melakukan deepcopy, tetapi jangan lakukan itu:
problematic_deep_copy = eval(repr(a_list))
Dalam 64 bit Python 2.7:
>>> import timeit
>>> import copy
>>> l = range(10)
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
27.55826997756958
>>> min(timeit.repeat(lambda: eval(repr(l))))
29.04534101486206
pada 64 bit Python 3.5:
>>> import timeit
>>> import copy
>>> l = list(range(10))
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
16.84255409205798
>>> min(timeit.repeat(lambda: eval(repr(l))))
34.813894678023644
list_copy=[]
for item in list: list_copy.append(copy(item))
dan jauh lebih cepat.
Ada banyak jawaban yang memberi tahu Anda cara membuat salinan yang tepat, tetapi tidak ada yang mengatakan mengapa 'salinan' asli Anda gagal.
Python tidak menyimpan nilai dalam variabel; itu mengikat nama ke objek. Tugas asli Anda mengambil objek yang dirujuk oleh my_list
dan mengikatnya new_list
juga. Apa pun nama yang Anda gunakan, hanya ada satu daftar, jadi perubahan yang dibuat saat merujuknya my_list
akan tetap ada saat merujuknya sebagai new_list
. Masing-masing jawaban lain untuk pertanyaan ini memberi Anda cara berbeda untuk membuat objek baru untuk diikat new_list
.
Setiap elemen daftar bertindak seperti nama, di mana setiap elemen mengikat secara non-eksklusif ke objek. Salinan dangkal membuat daftar baru yang elemennya mengikat objek yang sama seperti sebelumnya.
new_list = list(my_list) # or my_list[:], but I prefer this syntax
# is simply a shorter way of:
new_list = [element for element in my_list]
Untuk membawa salinan daftar Anda satu langkah lebih jauh, salin setiap objek yang dirujuk daftar Anda, dan ikat salinan elemen tersebut ke daftar baru.
import copy
# each element must have __copy__ defined for this...
new_list = [copy.copy(element) for element in my_list]
Ini belum merupakan salinan yang dalam, karena setiap elemen daftar dapat merujuk ke objek lain, sama seperti daftar terikat ke elemen-elemennya. Untuk secara rekursif menyalin setiap elemen dalam daftar, dan kemudian masing-masing objek lain disebut oleh masing-masing elemen, dan seterusnya: melakukan salinan yang dalam.
import copy
# each element must have __deepcopy__ defined for this...
new_list = copy.deepcopy(my_list)
Lihat dokumentasi untuk informasi lebih lanjut tentang kasus sudut dalam penyalinan.
Menggunakan thing[:]
>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>>
Mari kita mulai dari awal dan mengeksplorasi pertanyaan ini.
Jadi anggaplah Anda memiliki dua daftar:
list_1=['01','98']
list_2=[['01','98']]
Dan kita harus menyalin kedua daftar, sekarang mulai dari daftar pertama:
Jadi pertama mari kita coba dengan menetapkan variabel copy
ke daftar asli kami, list_1
:
copy=list_1
Sekarang jika Anda berpikir menyalin menyalin list_1, maka Anda salah. The id
Fungsi dapat menunjukkan kepada kita jika dua variabel dapat menunjuk ke objek yang sama. Mari kita coba ini:
print(id(copy))
print(id(list_1))
Outputnya adalah:
4329485320
4329485320
Kedua variabel tersebut adalah argumen yang sama persis. Apakah kamu terkejut?
Jadi seperti yang kita tahu python tidak menyimpan apa pun dalam variabel, Variabel hanya merujuk ke objek dan objek menyimpan nilainya. Di sini objek adalah list
tetapi kami membuat dua referensi ke objek yang sama dengan dua nama variabel yang berbeda. Ini berarti bahwa kedua variabel menunjuk ke objek yang sama, hanya dengan nama yang berbeda.
Ketika Anda melakukannya copy=list_1
, itu sebenarnya dilakukan:
Di sini, di daftar gambar_1 dan salin adalah dua nama variabel tetapi objeknya sama untuk kedua variabel tersebut list
Jadi, jika Anda mencoba mengubah daftar yang disalin maka itu akan mengubah daftar asli juga karena daftar itu hanya ada di sana, Anda akan memodifikasi daftar itu, apa pun yang Anda lakukan dari daftar yang disalin atau dari daftar asli:
copy[0]="modify"
print(copy)
print(list_1)
keluaran:
['modify', '98']
['modify', '98']
Jadi itu memodifikasi daftar asli:
Sekarang mari kita beralih ke metode pythonic untuk menyalin daftar.
copy_1=list_1[:]
Metode ini memperbaiki masalah pertama yang kami miliki:
print(id(copy_1))
print(id(list_1))
4338792136
4338791432
Jadi seperti yang kita lihat daftar kedua kami memiliki id yang berbeda dan itu berarti bahwa kedua variabel menunjuk ke objek yang berbeda. Jadi yang sebenarnya terjadi di sini adalah:
Sekarang mari kita coba memodifikasi daftar dan mari kita lihat apakah kita masih menghadapi masalah sebelumnya:
copy_1[0]="modify"
print(list_1)
print(copy_1)
Outputnya adalah:
['01', '98']
['modify', '98']
Seperti yang Anda lihat, itu hanya mengubah daftar yang disalin. Itu artinya berhasil.
Apakah Anda pikir kita sudah selesai? Tidak. Mari kita coba menyalin daftar bersarang kita.
copy_2=list_2[:]
list_2
harus merujuk ke objek lain yang merupakan salinan list_2
. Mari kita periksa:
print(id((list_2)),id(copy_2))
Kami mendapatkan output:
4330403592 4330403528
Sekarang kita dapat mengasumsikan kedua daftar menunjuk objek yang berbeda, jadi sekarang mari kita coba memodifikasinya dan mari kita lihat memberikan apa yang kita inginkan:
copy_2[0][1]="modify"
print(list_2,copy_2)
Ini memberi kami output:
[['01', 'modify']] [['01', 'modify']]
Ini mungkin tampak sedikit membingungkan, karena metode yang sama yang kami gunakan sebelumnya bekerja Mari kita coba memahami ini.
Saat kamu melakukan:
copy_2=list_2[:]
Anda hanya menyalin daftar luar, bukan daftar dalam. Kita dapat menggunakan id
fungsi sekali lagi untuk memeriksa ini.
print(id(copy_2[0]))
print(id(list_2[0]))
Outputnya adalah:
4329485832
4329485832
Ketika kita melakukannya copy_2=list_2[:]
, ini terjadi:
Itu membuat salinan daftar tetapi hanya salinan daftar luar, bukan salinan daftar bersarang, daftar bersarang sama untuk kedua variabel, jadi jika Anda mencoba untuk memodifikasi daftar bersarang maka itu akan mengubah daftar asli juga karena objek daftar bersarang adalah sama untuk kedua daftar.
Apa solusinya? Solusinya adalah deepcopy
fungsinya.
from copy import deepcopy
deep=deepcopy(list_2)
Mari kita periksa ini:
print(id((list_2)),id(deep))
4322146056 4322148040
Kedua daftar luar memiliki ID yang berbeda, mari kita coba ini pada daftar bersarang dalam.
print(id(deep[0]))
print(id(list_2[0]))
Outputnya adalah:
4322145992
4322145800
Karena Anda dapat melihat kedua ID berbeda, artinya kita dapat mengasumsikan bahwa kedua daftar bersarang menunjuk objek yang berbeda sekarang.
Ini berarti ketika Anda melakukan deep=deepcopy(list_2)
apa yang sebenarnya terjadi:
Kedua daftar bersarang menunjuk objek yang berbeda dan mereka memiliki salinan daftar daftar terpisah sekarang.
Sekarang mari kita coba untuk memodifikasi daftar bersarang dan melihat apakah itu memecahkan masalah sebelumnya atau tidak:
deep[0][1]="modify"
print(list_2,deep)
Ini menghasilkan:
[['01', '98']] [['01', 'modify']]
Seperti yang Anda lihat, itu tidak mengubah daftar bersarang asli, itu hanya mengubah daftar yang disalin.
Berikut adalah hasil pengaturan waktu menggunakan Python 3.6.8. Ingatlah bahwa saat-saat ini relatif satu sama lain, bukan absolut.
Saya terjebak untuk hanya melakukan salinan dangkal, dan juga menambahkan beberapa metode baru yang tidak mungkin di Python2, seperti list.copy()
( irisan setara Python3 ) dan dua bentuk daftar membongkar ( *new_list, = list
dan new_list = [*list]
):
METHOD TIME TAKEN
b = [*a] 2.75180600000021
b = a * 1 3.50215399999990
b = a[:] 3.78278899999986 # Python2 winner (see above)
b = a.copy() 4.20556500000020 # Python3 "slice equivalent" (see above)
b = []; b.extend(a) 4.68069800000012
b = a[0:len(a)] 6.84498999999959
*b, = a 7.54031799999984
b = list(a) 7.75815899999997
b = [i for i in a] 18.4886440000000
b = copy.copy(a) 18.8254879999999
b = []
for item in a:
b.append(item) 35.4729199999997
Kita dapat melihat pemenang Python2 masih bekerja dengan baik, tetapi tidak mendukung Python3 list.copy()
banyak , terutama mengingat keterbacaan superior dari yang terakhir.
Kuda hitam adalah metode membongkar dan mengemas ( b = [*a]
), yang ~ 25% lebih cepat daripada mengiris mentah, dan lebih dari dua kali lebih cepat dari metode membongkar lainnya ( *b, = a
).
b = a * 1
juga sangat baik.
Perhatikan bahwa metode ini tidak menghasilkan hasil yang setara untuk input selain dari daftar. Mereka semua bekerja untuk objek yang dapat diiris, beberapa bekerja untuk setiap iterable, tetapi hanyacopy.copy()
bekerja untuk objek Python yang lebih umum.
Berikut adalah kode pengujian untuk pihak yang berkepentingan ( Templat dari sini ):
import timeit
COUNT = 50000000
print("Array duplicating. Tests run", COUNT, "times")
setup = 'a = [0,1,2,3,4,5,6,7,8,9]; import copy'
print("b = list(a)\t\t", timeit.timeit(stmt='b = list(a)', setup=setup, number=COUNT))
print("b = copy.copy(a)\t", timeit.timeit(stmt='b = copy.copy(a)', setup=setup, number=COUNT))
print("b = a.copy()\t\t", timeit.timeit(stmt='b = a.copy()', setup=setup, number=COUNT))
print("b = a[:]\t\t", timeit.timeit(stmt='b = a[:]', setup=setup, number=COUNT))
print("b = a[0:len(a)]\t\t", timeit.timeit(stmt='b = a[0:len(a)]', setup=setup, number=COUNT))
print("*b, = a\t\t\t", timeit.timeit(stmt='*b, = a', setup=setup, number=COUNT))
print("b = []; b.extend(a)\t", timeit.timeit(stmt='b = []; b.extend(a)', setup=setup, number=COUNT))
print("b = []; for item in a: b.append(item)\t", timeit.timeit(stmt='b = []\nfor item in a: b.append(item)', setup=setup, number=COUNT))
print("b = [i for i in a]\t", timeit.timeit(stmt='b = [i for i in a]', setup=setup, number=COUNT))
print("b = [*a]\t\t", timeit.timeit(stmt='b = [*a]', setup=setup, number=COUNT))
print("b = a * 1\t\t", timeit.timeit(stmt='b = a * 1', setup=setup, number=COUNT))
b=[*a]
- satu-satunya cara yang jelas untuk melakukannya;).
Semua kontributor lain memberikan jawaban yang bagus , yang bekerja ketika Anda memiliki daftar satu dimensi (diratakan), namun metode yang disebutkan sejauh ini, hanya copy.deepcopy()
berfungsi untuk mengkloning / menyalin daftar dan tidak mengarahkannya ke list
objek bersarang saat Anda berada. bekerja dengan multidimensi, daftar bersarang (daftar daftar). Sementara Felix Kling merujuknya dalam jawabannya, ada sedikit lebih banyak untuk masalah ini dan mungkin solusi menggunakan built-in yang mungkin membuktikan alternatif yang lebih cepat deepcopy
.
Sementara new_list = old_list[:]
, copy.copy(old_list)'
dan untuk Py3k old_list.copy()
bekerja untuk daftar level tunggal, mereka kembali menunjuk ke list
objek yang bersarang di dalam old_list
dan new_list
, dan mengubah ke salah satulist
objek diabadikan di yang lain.
Seperti yang ditunjukkan oleh Aaron Hall dan PM 2Ring menggunakan
eval()
tidak hanya ide yang buruk, itu juga jauh lebih lambat daripadacopy.deepcopy()
.Ini berarti bahwa untuk daftar multidimensi, satu-satunya pilihan adalah
copy.deepcopy()
. Dengan itu dikatakan, itu benar-benar bukan pilihan karena kinerja berjalan jauh ke selatan ketika Anda mencoba menggunakannya pada array multidimensi berukuran sedang. saya mencoba untuktimeit
menggunakan array 42x42, tidak pernah terdengar atau bahkan sebesar itu untuk aplikasi bioinformatika, dan saya menyerah menunggu jawaban dan baru mulai mengetikkan edit saya ke posting ini.Tampaknya satu-satunya pilihan nyata adalah menginisialisasi banyak daftar dan bekerja secara mandiri. Jika ada yang punya saran lain, untuk bagaimana menangani penyalinan daftar multidimensi, itu akan dihargai.
Seperti yang telah dinyatakan orang lain, ada masalah kinerja yang signifikan menggunakan copy
modul dan copy.deepcopy
untuk daftar multidimensi .
repr()
cukup untuk membuat ulang objek. Juga, eval()
merupakan alat pilihan terakhir; lihat Eval benar-benar berbahaya oleh veteran SO Ned Batchelder untuk detailnya. Jadi, ketika Anda menganjurkan penggunaan eval()
Anda benar - benar harus menyebutkan bahwa itu bisa berbahaya.
eval()
fungsi dalam Python secara umum adalah risiko. Itu tidak begitu banyak apakah Anda menggunakan fungsi dalam kode tetapi itu adalah lubang keamanan di Python dalam dan dari dirinya sendiri. Misalnya saya tidak menggunakannya dengan fungsi yang menerima masukan dari input()
, sys.agrv
atau bahkan file teks. Itu lebih sepanjang garis menginisialisasi daftar multidimensi kosong sekali, dan kemudian hanya memiliki cara menyalinnya dalam satu lingkaran daripada menginisialisasi ulang pada setiap iterasi dari loop.
new_list = eval(repr(old_list))
, jadi selain itu adalah ide yang buruk, mungkin juga terlalu lambat untuk bekerja.
Ini mengejutkan saya bahwa ini belum disebutkan, jadi demi kelengkapan ...
Anda dapat melakukan pembongkaran daftar dengan "operator percikan":, *
yang juga akan menyalin elemen daftar Anda.
old_list = [1, 2, 3]
new_list = [*old_list]
new_list.append(4)
old_list == [1, 2, 3]
new_list == [1, 2, 3, 4]
Kelemahan yang jelas untuk metode ini adalah bahwa itu hanya tersedia dalam Python 3.5+.
Meskipun demikian, pengaturan waktu tampaknya lebih baik daripada metode umum lainnya.
x = [random.random() for _ in range(1000)]
%timeit a = list(x)
%timeit a = x.copy()
%timeit a = x[:]
%timeit a = [*x]
#: 2.47 µs ± 38.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.47 µs ± 54.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.39 µs ± 58.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.22 µs ± 43.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
old_list
dan new_list
dua daftar berbeda, mengedit satu tidak akan mengubah yang lain (kecuali jika Anda secara langsung mengubah elemen itu sendiri (seperti daftar daftar), tidak satupun dari metode ini adalah salinan yang dalam).
Sebuah pendekatan yang sangat sederhana tanpa versi python telah hilang dalam jawaban yang sudah diberikan yang dapat Anda gunakan sebagian besar waktu (setidaknya saya lakukan):
new_list = my_list * 1 #Solution 1 when you are not using nested lists
Namun, Jika my_list berisi wadah lain (misalnya, daftar bertingkat), Anda harus menggunakan deepcopy seperti yang disarankan dalam jawaban di atas dari perpustakaan salinan. Sebagai contoh:
import copy
new_list = copy.deepcopy(my_list) #Solution 2 when you are using nested lists
. Bonus : Jika Anda tidak ingin menyalin elemen gunakan (alias salinan dangkal):
new_list = my_list[:]
Mari kita pahami perbedaan antara Solusi # 1 dan Solusi # 2
>>> a = range(5)
>>> b = a*1
>>> a,b
([0, 1, 2, 3, 4], [0, 1, 2, 3, 4])
>>> a[2] = 55
>>> a,b
([0, 1, 55, 3, 4], [0, 1, 2, 3, 4])
Seperti yang Anda lihat Solusi # 1 bekerja dengan sempurna ketika kami tidak menggunakan daftar bersarang. Mari kita periksa apa yang akan terjadi ketika kita menerapkan solusi # 1 ke daftar bersarang.
>>> from copy import deepcopy
>>> a = [range(i,i+4) for i in range(3)]
>>> a
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> b = a*1
>>> c = deepcopy(a)
>>> for i in (a, b, c): print i
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> a[2].append('99')
>>> for i in (a, b, c): print i
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]] #Solution#1 didn't work in nested list
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]] #Solution #2 - DeepCopy worked in nested list
Perhatikan bahwa ada beberapa kasus di mana jika Anda telah menetapkan kelas kustom Anda sendiri dan Anda ingin menyimpan atribut maka Anda harus menggunakan copy.copy()
atau copy.deepcopy()
bukan alternatif, misalnya dalam Python 3:
import copy
class MyList(list):
pass
lst = MyList([1,2,3])
lst.name = 'custom list'
d = {
'original': lst,
'slicecopy' : lst[:],
'lstcopy' : lst.copy(),
'copycopy': copy.copy(lst),
'deepcopy': copy.deepcopy(lst)
}
for k,v in d.items():
print('lst: {}'.format(k), end=', ')
try:
name = v.name
except AttributeError:
name = 'NA'
print('name: {}'.format(name))
Output:
lst: original, name: custom list
lst: slicecopy, name: NA
lst: lstcopy, name: NA
lst: copycopy, name: custom list
lst: deepcopy, name: custom list
new_list = my_list[:]
new_list = my_list
Coba pahami ini. Katakanlah my_list ada di memori tumpukan di lokasi X yaitu my_list menunjuk ke X. Sekarang dengan menetapkan new_list = my_list
Anda membiarkan New_list menunjuk ke X. Ini dikenal sebagai Copy dangkal.
Sekarang jika Anda menetapkan new_list = my_list[:]
Anda hanya menyalin setiap objek dari my_list ke new_list. Ini dikenal sebagai Deep copy.
Cara lain yang dapat Anda lakukan adalah:
new_list = list(old_list)
import copy
new_list = copy.deepcopy(old_list)
Saya ingin memposting sesuatu yang sedikit berbeda dari beberapa jawaban lainnya. Meskipun ini kemungkinan besar bukan opsi yang paling mudah dipahami, atau tercepat, ia memberikan sedikit pandangan ke dalam tentang seberapa dalam copy berfungsi, serta menjadi pilihan alternatif lain untuk penyalinan dalam. Tidak masalah jika fungsi saya memiliki bug, karena intinya adalah menunjukkan cara untuk menyalin objek seperti jawaban pertanyaan, tetapi juga menggunakan ini sebagai titik untuk menjelaskan cara kerja deepcopy pada intinya.
Inti dari setiap fungsi penyalinan yang dalam adalah cara untuk membuat salinan yang dangkal. Bagaimana? Sederhana. Setiap fungsi salin yang dalam hanya menduplikasi wadah dari objek yang tidak dapat diubah. Ketika Anda menyalin daftar bersarang, Anda hanya menduplikasi daftar luar, bukan objek yang bisa berubah di dalam daftar. Anda hanya menduplikasi kontainer. Hal yang sama juga berlaku untuk kelas. Saat Anda mendalami suatu kelas, Anda mendokumentasikan semua atributnya yang bisa berubah. Jadi bagaimana? Kenapa Anda hanya perlu menyalin wadah, seperti daftar, dicts, tuple, iters, kelas, dan instance kelas?
Itu mudah. Objek yang bisa berubah tidak dapat benar-benar diduplikasi. Itu tidak pernah bisa diubah, jadi itu hanya nilai tunggal. Itu berarti Anda tidak perlu menduplikasi string, angka, bools, atau yang lainnya. Tetapi bagaimana Anda akan menduplikasi kontainer? Sederhana. Anda hanya membuat inisialisasi wadah baru dengan semua nilai. Deepcopy bergantung pada rekursi. Ini menduplikasi semua wadah, bahkan yang memiliki wadah di dalamnya, sampai tidak ada wadah yang tersisa. Wadah adalah benda abadi.
Setelah Anda tahu itu, menduplikasi objek sepenuhnya tanpa referensi apa pun cukup mudah. Inilah fungsi untuk menyalin tipe data dasar (tidak akan berfungsi untuk kelas khusus tetapi Anda selalu dapat menambahkannya)
def deepcopy(x):
immutables = (str, int, bool, float)
mutables = (list, dict, tuple)
if isinstance(x, immutables):
return x
elif isinstance(x, mutables):
if isinstance(x, tuple):
return tuple(deepcopy(list(x)))
elif isinstance(x, list):
return [deepcopy(y) for y in x]
elif isinstance(x, dict):
values = [deepcopy(y) for y in list(x.values())]
keys = list(x.keys())
return dict(zip(keys, values))
Deepcopy bawaan Python sendiri didasarkan pada contoh itu. Satu-satunya perbedaan adalah mendukung tipe lain, dan juga mendukung kelas pengguna dengan menduplikasi atribut menjadi kelas duplikat baru, dan juga memblokir rekursi tak terbatas dengan referensi ke objek yang sudah terlihat menggunakan daftar memo atau kamus. Dan itu benar-benar untuk membuat salinan yang dalam. Pada intinya, membuat salinan yang dalam hanya membuat salinan yang dangkal. Saya harap jawaban ini menambah sesuatu pada pertanyaan.
CONTOH
Katakanlah Anda memiliki daftar ini: [1, 2, 3] . Angka-angka yang tidak dapat diubah tidak dapat diduplikasi, tetapi lapisan lainnya bisa. Anda dapat menduplikatnya menggunakan pemahaman daftar: [x untuk x dalam [1, 2, 3]
Sekarang, bayangkan Anda memiliki daftar ini: [[1, 2], [3, 4], [5, 6]] . Kali ini, Anda ingin membuat fungsi, yang menggunakan rekursi untuk menyalin semua lapisan daftar. Alih-alih pemahaman daftar sebelumnya:
[x for x in _list]
Ini menggunakan yang baru untuk daftar:
[deepcopy_list(x) for x in _list]
Dan deepcopy_list terlihat seperti ini:
def deepcopy_list(x):
if isinstance(x, (str, bool, float, int)):
return x
else:
return [deepcopy_list(y) for y in x]
Maka sekarang Anda memiliki fungsi yang dapat menyalin semua daftar str, bools, floast, int dan bahkan daftar ke banyak lapisan tanpa batas menggunakan rekursi. Dan begitulah, deepcopying.
TLDR : Deepcopy menggunakan rekursi untuk menduplikasi objek, dan hanya mengembalikan objek yang sama seperti sebelumnya, karena objek yang tidak dapat diubah tidak dapat diduplikasi. Namun, ia menduplikasi lapisan paling dalam dari objek yang bisa berubah hingga mencapai lapisan paling bisa berubah dari suatu objek.
Perspektif praktis sedikit untuk melihat ke memori melalui id dan gc.
>>> b = a = ['hell', 'word']
>>> c = ['hell', 'word']
>>> id(a), id(b), id(c)
(4424020872, 4424020872, 4423979272)
| |
-----------
>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # all referring to same 'hell'
| | |
-----------------------
>>> id(a[0][0]), id(b[0][0]), id(c[0][0])
(4422785208, 4422785208, 4422785208) # all referring to same 'h'
| | |
-----------------------
>>> a[0] += 'o'
>>> a,b,c
(['hello', 'word'], ['hello', 'word'], ['hell', 'word']) # b changed too
>>> id(a[0]), id(b[0]), id(c[0])
(4424018384, 4424018384, 4424018328) # augmented assignment changed a[0],b[0]
| |
-----------
>>> b = a = ['hell', 'word']
>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # the same hell
| | |
-----------------------
>>> import gc
>>> gc.get_referrers(a[0])
[['hell', 'word'], ['hell', 'word']] # one copy belong to a,b, the another for c
>>> gc.get_referrers(('hell'))
[['hell', 'word'], ['hell', 'word'], ('hell', None)] # ('hello', None)
Ingat itu dalam Python ketika Anda melakukannya:
list1 = ['apples','bananas','pineapples']
list2 = list1
List2 tidak menyimpan daftar sebenarnya, tetapi referensi ke list1. Jadi ketika Anda melakukan apa saja untuk list1, list2 berubah juga. gunakan modul salin (bukan default, unduh di pip) untuk membuat salinan asli dari daftar ( copy.copy()
untuk daftar sederhana, copy.deepcopy()
untuk yang bersarang). Ini membuat salinan yang tidak berubah dengan daftar pertama.
Opsi deepcopy adalah satu-satunya metode yang bekerja untuk saya:
from copy import deepcopy
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = deepcopy(a)
b[0][1]=[3]
print('Deep:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a*1
b[0][1]=[3]
print('*1:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a[:]
b[0][1]=[3]
print('Vector copy:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = list(a)
b[0][1]=[3]
print('List copy:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a.copy()
b[0][1]=[3]
print('.copy():')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a
b[0][1]=[3]
print('Shallow:')
print(a)
print(b)
print('-----------------------------')
mengarah ke hasil:
Deep:
[[[1, 2], [1, 2], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
*1:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Vector copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
List copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
.copy():
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Shallow:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Ini karena, baris new_list = my_list
memberikan referensi baru ke variabel my_list
yang new_list
sama dengan C
kode yang diberikan di bawah ini,
int my_list[] = [1,2,3,4];
int *new_list;
new_list = my_list;
Anda harus menggunakan modul salin untuk membuat daftar baru
import copy
new_list = copy.deepcopy(my_list)
newlist = [*mylist]
juga kemungkinan di Python 3.newlist = list(mylist)
mungkin lebih jelas.