Soru Bir liste nasıl kopyalanır veya kopyalanır?


Python'da bir listeyi kopyalamak veya kopyalamak için seçenekler nelerdir?

kullanma new_list = my_list sonra değiştirir new_list her zaman my_list değişir.
Bu neden?


1698
2018-04-10 08:49


Menşei




Cevaplar:


İle new_list = my_listAslında iki listenin yok. Ödev yalnızca referansı gerçek listeye değil, listeye kopyalar. new_list ve my_list ödevden sonra aynı listeye bakın.

Listeyi kopyalamak için çeşitli olasılıklara sahipsiniz:

  • Yerleşik kullanabilirsiniz list.copy() yöntem (python 3.3'ten beri kullanılabilir):

    new_list = old_list.copy()
    
  • Dilimleyebilirsiniz:

    new_list = old_list[:]
    

    Alex Martelli'nin görüş (en azından 2007'de geri) bunun hakkında garip bir sözdizimi ve bunu kullanmak hiç mantıklı değil. ;) (Onun görüşüne göre, bir sonraki daha okunabilir).

  • Yerleşik kullanabilirsiniz list() fonksiyon:

    new_list = list(old_list)
    
  • Jenerik kullanabilirsiniz copy.copy():

    import copy
    new_list = copy.copy(old_list)
    

    Bu biraz daha yavaş list() çünkü veri türünü bulmak zorundadır old_list ilk.

  • Liste nesneleri içeriyorsa ve bunları da kopyalamak istiyorsanız, jenerik kullanın copy.deepcopy():

    import copy
    new_list = copy.deepcopy(old_list)
    

    Açıkçası, en yavaş ve en çok bellek gerektiren yöntem, ancak bazen kaçınılmaz.

Örnek:

import copy

class Foo(object):
    def __init__(self, val):
         self.val = val

    def __repr__(self):
        return str(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\n list.copy(): %r\n slice: %r\n list(): %r\n copy: %r\n deepcopy: %r'
      % (a, b, c, d, e, f))

Sonuç:

original: ['foo', 5, 'baz']
list.copy(): ['foo', 5]
slice: ['foo', 5]
list(): ['foo', 5]
copy: ['foo', 5]
deepcopy: ['foo', 1]

2326
2018-04-10 08:55



@FelixKling: Bu cevabı düzenlemek için mantıklı olur mu list.copy yöntem (Python 3.3'ten beri kullanılabilir)? Python 2 uyumluluğuna ihtiyacınız yoksa, bunu yapmak bir gerçek yoldur. - Mark Dickinson
@FelixKling I% 100 katılıyorum. Önemli bir Python sorusuna bir cevap için, bu biraz dağınık ve güncel değil. - Jiminion
Eğer benimle dalga geçmiyorsam: newlist = [*mylist] Python 3'te de bir olasılıktır. newlist = list(mylist) belki de daha açık. - Stéphane
başka bir olasılık new_list = eski_listedir * 1 - aris


Felix zaten mükemmel bir cevap verdi, ancak çeşitli yöntemlerin hız karşılaştırmasını yapacağımı düşündüm:

  1. 10,59 sn (105,9us / itn) - copy.deepcopy(old_list)
  2. 10.16 sn (101.6us / itn) - saf python Copy() deepcopy ile sınıfları kopyalama yöntemi
  3. 1.488 sn (14.88us / itn) - saf python Copy() sınıfları kopyalama yöntemi (sadece dikte / liste / tuples)
  4. 0,325 sn (3,25us / itn) - for item in old_list: new_list.append(item)
  5. 0.217 sn (2.17us / itn) - [i for i in old_list] (a liste anlama)
  6. 0.186 sn (1.86us / itn) - copy.copy(old_list)
  7. 0,075 sn (0,75us / itn) - list(old_list)
  8. 0,053 sn (0,53us / itn) - new_list = []; new_list.extend(old_list)
  9. 0.039 sn (0.39us / itn) - old_list[:] (liste dilimleme)

Yani en hızlı liste dilimleme. Ama bunun farkında ol copy.copy(), list[:] ve list(list)aksine copy.deepcopy() ve python sürümü listedeki herhangi bir listeyi, sözlükleri ve sınıf örneklerini kopyalamaz, böylece orijinaller değişirse, kopyalanan listede de değişecektir ve bunun tersi de geçerlidir.

(Birisi ilgilendiyse veya herhangi bir sorunu dile getirmek istiyorsa komut dosyası şöyledir :)

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

DÜZENLE: Yeni stil, eski stil sınıfları ve dikte dikenlerine eklendi, python versiyonunu çok daha hızlı yaptı ve liste ifadeleri dahil bazı yöntemler ekledi. extend().


449
2018-04-10 10:16



Karşılaştırma yaptığınız için referans noktası eklemenize yardımcı olabilir. Bu rakamlar, 2017 yılında tamamen derlenmiş kod ile Python 3.6 kullanılarak hala doğru mu? Aşağıdaki cevabı görmüyorum (stackoverflow.com/a/17810305/26219) Bu cevabı zaten soruyor. - Mark Edington
kullan timeit modülü. Ayrıca, bunun gibi keyfi mikro ölçütlerden çok fazla sonuç alamazsınız. - Corey Goldberg


Ya sahibim söylendi bu Python 3,3 + ekler list.copy() dilimleme kadar hızlı olması gereken yöntem:

newlist = old_list.copy()


116
2017-07-23 12:32





Python'da bir listeyi kopyalamak veya kopyalamak için seçenekler nelerdir?

Python 3'te, aşağıdakilerle sığ bir kopya yapılabilir:

a_copy = a_list.copy()

Python 2 ve 3'te, orijinalin tam bir dilimi ile sığ bir kopya alabilirsiniz:

a_copy = a_list[:]

açıklama

Bir listeyi kopyalamak için iki anlamsal yol vardır. Bir sığ kopya aynı nesnelerin yeni bir listesini oluşturur, derin bir kopya yeni eşdeğer nesneleri içeren yeni bir liste oluşturur.

Sığ liste kopyası

Sığ bir kopya, listenin içindeki nesnelere referanslar listesi olan listenin kendisini kopyalar. İçerdiği nesneler değişebilir ve biri değişmişse, değişiklik her iki listeye de yansıtılacaktır.

Bunu Python 2 ve 3'te yapmanın farklı yolları vardır. Python 2 yolları da Python 3'te çalışacaktır.

Python 2

Python 2'de, bir listenin sığ bir kopyasını oluşturmanın deyimsel yolu, orijinalin tam bir dilimidir:

a_copy = a_list[:]

Listeyi liste oluşturucudan geçirerek de aynı şeyi yapabilirsiniz.

a_copy = list(a_list)

ancak kurucuyu kullanmak daha az etkilidir:

>>> timeit
>>> l = range(20)
>>> min(timeit.repeat(lambda: l[:]))
0.30504298210144043
>>> min(timeit.repeat(lambda: list(l)))
0.40698814392089844

Python 3

Python 3'te listeler list.copy yöntem:

a_copy = a_list.copy()

Python 3.5’de:

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

Başka bir işaretçi yapmak değil Bir kopyasını çıkarmak

My_list'in her değiştiğinde new_list = my_list kullanarak yeni_list'i değiştirir. Bu neden?

my_list sadece hafızadaki gerçek listeye işaret eden bir isimdir. Dediğinde new_list = my_list Bir kopya oluşturmuyorsanız, yalnızca bu orijinal listede belleğe işaret eden başka bir ad ekliyorsunuz. Listelerin kopyalarını yaptığımızda benzer sorunlarımız olabilir.

>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]

Liste sadece içerik için bir dizi işaretçidir, bu yüzden sığ bir kopya sadece işaretçileri kopyalar ve böylece iki farklı listeniz vardır, ancak aynı içeriğe sahiptirler. İçeriğin kopyalarını yapmak için derin bir kopyasına ihtiyacınız var.

Derin kopyalar

Yapmak Bir listenin derin kopyası, Python 2 veya 3'te deepcopy içinde copy modül:

import copy
a_deep_copy = copy.deepcopy(a_list)

Bunun yeni alt listeler oluşturmamıza nasıl yardımcı olduğunu göstermek için:

>>> import copy
>>> l
[['foo'], [], []]
>>> l_deep_copy = copy.deepcopy(l)
>>> l_deep_copy[0].pop()
'foo'
>>> l_deep_copy
[[], [], []]
>>> l
[['foo'], [], []]

Ve böylece, derin kopyalanmış listenin orijinalden tamamen farklı bir liste olduğunu görüyoruz. Kendi işini yapabilirsin ama yapmazsın. Standart kütüphanenin derin kopyalama işlevini kullanarak sahip olamayacağınız hataları yaratmanız olasıdır.

Kullanma eval

Bunu, derin kopyalamanın bir yolu olarak görüyor olabilirsiniz, ancak yapmayın:

problematic_deep_copy = eval(repr(a_list))
  1. Tehlikeli, özellikle güvenmediğiniz bir kaynaktan bir şeyleri değerlendiriyorsanız.
  2. Kopyalama yaptığınız bir alt öğenin, eşdeğer bir öğeyi yeniden üretmenin değerlendirilebileceği bir temsili yoksa, güvenilir değildir.
  3. Aynı zamanda daha az performanslıdır.

64 bit Python 2.7'de:

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

64 bit Python 3.5 üzerinde:

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

88
2017-10-25 12:13





Doğru bir kopyanın nasıl yapıldığını anlatan çok sayıda yanıt var, ancak bunların hiçbiri orijinalinizin neden kopyalanmadığını söylüyor.

Python değerleri değişkenlerde saklamıyor; isimleri nesnelere bağlar. Orijinal atamanız, başvurulan nesneyi aldı my_list ve ona bağlı new_list de. Hangi ismi kullandığınız önemli değil, hala tek bir liste var. my_list ona atıfta bulunurken devam edecek new_list. Bu soruya verilen diğer yanıtların her biri, size bağlamak için yeni bir nesne oluşturmanın farklı yollarını sunar new_list.

Bir listenin her elemanı bir isim gibi davranır, çünkü her öğe bir nesneye münhasıran bağlanmaz. Sığ bir kopya, öğeleri daha önce olduğu gibi aynı nesnelere bağlanan yeni bir liste oluşturur.

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]

Listenizi bir adım ileriye taşımak için, listenizde atıfta bulunulan her nesneyi kopyalayın ve bu öğe kopyalarını yeni bir listeye bağlayın.

import copy  
# each element must have __copy__ defined for this...
new_list = [copy.copy(element) for element in my_list]

Bu henüz derin bir kopya değildir, çünkü bir listenin her bir öğesi, diğer öğelere atıfta bulunabilir, tıpkı listenin öğelerine bağlı olduğu gibi. Listedeki her öğeyi art arda kopyalamak ve sonra her bir öğe tarafından atıfta bulunulan diğer her bir nesneyi kopyalamak için: derin bir kopya gerçekleştirin.

import copy
# each element must have __deepcopy__ defined for this...
new_list = copy.deepcopy(my_list)

Görmek Dökümantasyon kopyalamada köşe durumları hakkında daha fazla bilgi için.


42
2017-11-23 16:45





new_list = list(old_list)


30
2018-04-10 09:03





kullanım thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 

27
2018-04-10 08:53





Bunu yapmak için Python’un deyimidir newList = oldList[:]


26
2018-04-10 08:53