Lijst verandert onverwacht na toewijzing. Waarom is dit en hoe kan ik het voorkomen?

Tijdens het gebruik van new_list = my_list, veranderen alle wijzigingen aan new_listtelkens my_list. Waarom is dit en hoe kan ik de lijst klonen of kopiëren om dit te voorkomen?


Antwoord 1, autoriteit 100%

Met new_list = my_listheb je eigenlijk geen twee lijsten. De opdracht kopieert alleen de verwijzing naar de lijst, niet de eigenlijke lijst, dus zowel new_listals my_listverwijzen naar dezelfde lijst na de opdracht.

Om de lijst daadwerkelijk te kopiëren, heb je verschillende mogelijkheden:

  • U kunt de ingebouwde list.copy()methode (beschikbaar sinds Python 3.3):

    new_list = old_list.copy()
    
  • Je kunt het in stukjes snijden:

    new_list = old_list[:]
    

    Alex Martelli’smening (tenminste in 2007) hierover is, dat het is een rare syntaxis en het heeft geen zin om het ooit te gebruiken. 😉 (Naar zijn mening is de volgende beter leesbaar).

  • Je kunt de ingebouwde list()functie:

    new_list = list(old_list)
    
  • Je kunt generieke gebruiken copy.copy():

    import copy
    new_list = copy.copy(old_list)
    

    Dit is iets langzamer dan list()omdat het eerst het datatype van old_listmoet achterhalen.

  • Als de lijst objecten bevat en u deze ook wilt kopiëren, gebruikt u algemene copy.deepcopy():

    import copy
    new_list = copy.deepcopy(old_list)
    

    Natuurlijk de langzaamste methode die veel geheugen nodig heeft, maar soms onvermijdelijk.

Voorbeeld:

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

Resultaat:

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

Antwoord 2, autoriteit 19%

Felix gaf al een uitstekend antwoord, maar ik dacht dat ik een snelheidsvergelijking zou maken van de verschillende methoden:

  1. 10,59 sec (105,9 µs/itn) – copy.deepcopy(old_list)
  2. 10.16 sec (101,6 µs/itn) – pure Python Copy()methode kopieerklassen met deepcopy
  3. 1.488 sec (14.88 µs/itn) – pure Python Copy()methode kopieert geen klassen (alleen dicts/lists/tuples)
  4. 0.325 sec (3.25 µs/itn) – for item in old_list: new_list.append(item)
  5. 0,217 sec (2,17 µs/itn) – [i for i in old_list](a begrip van de lijst)
  6. 0,186 sec (1,86 µs/itn) – copy.copy(old_list)
  7. 0,075 sec (0,75 µs/itn) – list(old_list)
  8. 0,053 sec (0,53 µs/itn) – new_list = []; new_list.extend(old_list)
  9. 0.039 sec (0.39 µs/itn) – old_list[:](lijst snijden)

Dus de snelste is list slicing. Maar houd er rekening mee dat copy.copy(), list[:]en list(list), in tegenstelling tot copy.deepcopy()en de python-versie kopiëren geen lijsten, woordenboeken en klasse-instanties in de lijst, dus als de originelen veranderen, veranderen ze ook in de gekopieerde lijst en vice versa.

(Hier is het script als iemand geïnteresseerd is of problemen wil melden:)

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

3, Autoriteit 5%

Ik heb verteld die python 3.3+ voegt de list.copy()methode, die zou moeten zijn Snel als snijden:

newlist = old_list.copy()

4, Autoriteit 4%

Wat zijn de opties om een ​​lijst in Python te klonen of te kopiëren?

In Python 3 kan een ondiepe kopie worden gemaakt met:

a_copy = a_list.copy()

In Python 2 en 3 kunt u een ondiepe kopie krijgen met een volledige plak van het origineel:

a_copy = a_list[:]

Uitleg

Er zijn twee semantische manieren om een ​​lijst te kopiëren. Een ondiepe kopie maakt een nieuwe lijst met dezelfde objecten, een diepe kopie maakt een nieuwe lijst met nieuwe equivalente objecten.

Ondiepe lijst Kopieer

Een oppervlakkige kopie kopieert alleen de lijst zelf, wat een container is met verwijzingen naar de objecten in de lijst. Als de objecten zelf veranderlijk zijn en er één wordt gewijzigd, wordt de wijziging in beide lijsten weergegeven.

Er zijn verschillende manieren om dit te doen in Python 2 en 3. De Python 2-manieren werken ook in Python 3.

Python 2

In Python 2 is de idiomatische manier om een ondiepe kopie van een lijst te maken met een compleet stuk van het origineel:

a_copy = a_list[:]

U kunt hetzelfde bereiken door de lijst door de lijstconstructor te leiden,

a_copy = list(a_list)

maar het gebruik van de constructor is minder efficiënt:

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

Python 3

In Python 3 krijgen lijsten de methode list.copy:

a_copy = a_list.copy()

In 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

Het maken van een andere aanwijzer maakt geeneen kopie

Als je new_list = my_list gebruikt, wordt nieuwe_list elke keer gewijzigd als mijn_list verandert. Waarom is dit?

my_listis slechts een naam die verwijst naar de daadwerkelijke lijst in het geheugen. Als je new_list = my_listzegt, maak je geen kopie, maar voeg je gewoon een andere naam toe die verwijst naar die originele lijst in het geheugen. We kunnen soortgelijke problemen hebben wanneer we kopieën van lijsten maken.

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

De lijst is slechts een reeks verwijzingen naar de inhoud, dus een oppervlakkige kopie kopieert alleen de verwijzingen, en dus heb je twee verschillende lijsten, maar ze hebben dezelfde inhoud. Om kopieën van de inhoud te maken, heb je een diepe kopie nodig.

Diepe kopieën

Als u een diepe kopie van een lijst wilt maken, gebruikt u in Python 2 of 3 deepcopyin de module copy:

import copy
a_deep_copy = copy.deepcopy(a_list)

Om te demonstreren hoe we hierdoor nieuwe sublijsten kunnen maken:

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

En zo zien we dat de diep gekopieerde lijst een geheel andere lijst is dan het origineel. Je zou je eigen functie kunnen draaien, maar doe dat niet. U zult waarschijnlijk bugs creëren die u anders niet zou hebben door de deepcopy-functie van de standaardbibliotheek te gebruiken.

Gebruik geen eval

Misschien zie je dit als een manier om diep te kopiëren, maar doe het niet:

problematic_deep_copy = eval(repr(a_list))
  1. Het is gevaarlijk, vooral als je iets evalueert van een bron die je niet vertrouwt.
  2. Het is niet betrouwbaar als een subelement dat u kopieert geen representatie heeft die kan worden geëvalueerd om een equivalent element te reproduceren.
  3. Het is ook minder performant.

In 64-bits 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

op 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

Antwoord 5, autoriteit 2%

Er zijn al veel antwoorden die u vertellen hoe u een goede kopie kunt maken, maar geen van hen zegt waarom uw originele ‘kopie’ is mislukt.

Python slaat geen waarden op in variabelen; het bindt namen aan objecten. Je oorspronkelijke opdracht nam het object waarnaar wordt verwezen door my_listen koppelde het ook aan new_list. Welke naam je ook gebruikt, er is nog steeds maar één lijst, dus wijzigingen die worden aangebracht als je ernaar verwijst als my_list, blijven behouden als je ernaar verwijst als new_list. Elk van de andere antwoorden op deze vraag geeft je verschillende manieren om een nieuw object te maken om te binden aan new_list.

Elk element van een lijst werkt als een naam, in die zin dat elk element niet-exclusief aan een object bindt. Een oppervlakkige kopie creëert een nieuwe lijst waarvan de elementen binden aan dezelfde objecten als voorheen.

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]

Om uw lijstkopie nog een stap verder te brengen, kopieert u elk object waarnaar uw lijst verwijst en bindt u die elementkopieën aan een nieuwe lijst.

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

Dit is nog geen diepe kopie, omdat elk element van een lijst kan verwijzen naar andere objecten, net zoals de lijst aan zijn elementen is gebonden. Om elk element in de lijst recursief te kopiëren, en vervolgens elk ander object waarnaar door elk element wordt verwezen, enzovoort: voer een diepe kopie uit.

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

Zie de documentatievoor meer informatie over hoekgevallen bij kopiëren.

p>


6

Gebruik thing[:]

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

7

Idiom van Python voor het doen van dit is newList = oldList[:]


8

Alle andere bijdragers gaven geweldigeantwoorden, die werken als je een lijst met één dimensie (geëgaliseerd) hebt, maar van de methoden die tot nu toe zijn genoemd, alleen copy.deepcopy()werkt om een lijst te klonen/kopiëren en niet te laten verwijzen naar de geneste listobjecten wanneer u werkt met multidimensionale, geneste lijsten (lijst met lijsten). Terwijl Felix Klingernaar verwijst in zijn antwoord, is er iets meer aan de hand en mogelijk een tijdelijke oplossing met ingebouwde ins die een sneller alternatief kunnen zijn voor deepcopy.

Terwijl new_list = old_list[:], copy.copy(old_list)'en voor Py3k old_list.copy()werken voor enkele -leveled lijsten, ze keren terug naar het wijzen naar de listobjecten genest binnen de old_listen de new_list, en veranderen in een van de listobjecten worden bestendigd in de andere.

Bewerken: nieuwe informatie aan het licht gebracht

Zoals werd opgemerkt door zowel Aaron Hallals PM 2Ringhet gebruik van eval()is niet alleen een slecht idee, het is ook veel langzamer dan copy.deepcopy().

Dit betekent dat voor multidimensionale lijsten de enige optie copy.deepcopy()is. Dat gezegd hebbende, het is echt geen optie, omdat de prestaties ver naar het zuiden gaan als je het probeert te gebruiken op een middelgrote multidimensionale array. Ik probeerde te timeitmet een array van 42×42, niet ongehoord of zelfs zo groot voor bioinformatica-toepassingen, en ik gaf het wachten op een reactie op en begon gewoon mijn bewerking van dit bericht te typen.

Het lijkt erop dat de enige echte optie dan is om meerdere lijsten te initialiseren en er onafhankelijk aan te werken. Als iemand nog andere suggesties heeft voor het omgaan met het kopiëren van multidimensionale lijsten, wordt dat op prijs gesteld.

Zoals anderen al hebben aangegeven, zijn er aanzienlijkeprestatieproblemen bij het gebruik van de copy-module en copy.deepcopyvoor multidimensionale lijsten.


Antwoord 9

Het verbaast me dat dit nog niet genoemd is, dus voor de volledigheid…

U kunt lijsten uitpakken met de “splat-operator”: *, die ook elementen van uw lijst kopieert.

old_list = [1, 2, 3]
new_list = [*old_list]
new_list.append(4)
old_list == [1, 2, 3]
new_list == [1, 2, 3, 4]

Het voor de hand liggende nadeel van deze methode is dat deze alleen beschikbaar is in Python 3.5+.

Toch qua timing lijkt dit beter te presteren dan andere veelgebruikte methoden.

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)

Antwoord 10

Merk op dat er enkele gevallen zijn waarin, als u uw eigen aangepaste klasse heeft gedefinieerd en u de kenmerken wilt behouden, u copy.copy()of copy.deepcopy()in plaats van de alternatieven, bijvoorbeeld in 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))

Uitgangen:

lst: original, name: custom list
lst: slicecopy, name: NA
lst: lstcopy, name: NA
lst: copycopy, name: custom list
lst: deepcopy, name: custom list

Antwoord 11

new_list = my_list[:]

new_list = my_list

Probeer dit te begrijpen. Laten we zeggen dat my_listzich in het heapgeheugen op locatie X bevindt, dwz dat my_listnaar de X wijst. Door nu new_list = my_listtoe te wijzen laat new_listnaar de X wijzen. Dit staat bekend als een ondiepe kopie.

Als u nu new_list = my_list[:]toewijst, kopieert u eenvoudig elk object van my_listnaar new_list. Dit staat bekend als een diepe kopie.

De anderemanier waarop u dit kunt doen zijn:

  • new_list = list(old_list)
  • import copy new_list = copy.deepcopy(old_list)

Antwoord 12

Onthoud dat in Python als je dat doet:

   list1 = ['apples','bananas','pineapples']
    list2 = list1

Lijst2 slaat niet de eigenlijke lijst op, maar een verwijzing naar lijst1. Dus als je iets aan lijst1 doet, verandert lijst2 ook. gebruik de kopieermodule (niet standaard, download op pip) om een originele kopie van de lijst te maken(copy.copy()voor eenvoudige lijsten, copy.deepcopy()voor geneste). Dit maakt een kopie die niet verandert met de eerste lijst.


Antwoord 13

Ik wilde iets anders posten dan sommige andere antwoorden. Hoewel dit hoogstwaarschijnlijk niet de meest begrijpelijke of snelste optie is, geeft het een beetje een idee van hoe diep kopiëren werkt, en is het ook een andere alternatieve optie voor diep kopiëren. Het maakt niet echt uit of mijn functie bugs heeft, aangezien het doel hiervan is om een manier te tonen om objecten zoals de vraagantwoorden te kopiëren, maar ook om dit te gebruiken als een punt om uit te leggen hoe deepcopy in de kern werkt.

De kern van elke diepe kopieerfunctie is de manier om een oppervlakkige kopie te maken. Hoe? Eenvoudig. Elke diepe kopieerfunctie dupliceert alleen de containers van onveranderlijke objecten. Wanneer u een geneste lijst diep kopieert, dupliceert u alleen de buitenste lijsten, niet de veranderlijke objecten binnen de lijsten. Je dupliceert alleen de containers. Hetzelfde geldt ook voor lessen. Wanneer je een klasse deepcopy doet, deepcopy je alle veranderlijke attributen. Dus hoe? Hoe komt het dat je alleen de containers hoeft te kopiëren, zoals lijsten, dicts, tuples, iters, klassen en klasse-instanties?

Het is eenvoudig. Een veranderlijk object kan niet echt worden gedupliceerd. Het kan nooit worden gewijzigd, dus het is slechts een enkele waarde. Dat betekent dat je nooit strings, getallen, boolen of iets van die dingen hoeft te dupliceren. Maar hoe zou u de containers dupliceren? Eenvoudig. U initialiseert gewoon een nieuwe container met alle waarden. Deepcopy vertrouwt op recursie. Het dupliceert alle containers, zelfs degene met containers erin, totdat er geen containers meer over zijn. Een container is een onveranderlijk object.

Als je dat eenmaal weet, is het vrij eenvoudig om een object volledig te dupliceren zonder enige verwijzing. Hier is een functie voor het diep kopiëren van basisgegevenstypes (zou niet werken voor aangepaste klassen, maar je zou dat altijd kunnen toevoegen)

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

Python’s eigen ingebouwde deepcopy is gebaseerd op dat voorbeeld. Het enige verschil is dat het andere typen ondersteunt en ook gebruikersklassen ondersteunt door de attributen te dupliceren in een nieuwe dubbele klasse, en ook oneindige recursie blokkeert met een verwijzing naar een object dat het al heeft gezien met behulp van een memolijst of woordenboek. En dat is het eigenlijk voor het maken van diepe kopieën. In de kern is het maken van een diepe kopie gewoon oppervlakkige kopieën maken. Ik hoop dat dit antwoord iets toevoegt aan de vraag.

VOORBEELDEN

Stel dat je deze lijst hebt: [1, 2, 3]. De onveranderlijke getallen kunnen niet worden gedupliceerd, maar de andere laag wel. Je kunt het dupliceren met behulp van een lijstbegrip: [x voor x in [1, 2, 3]

Stel je nu voor dat je deze lijst hebt: [[1, 2], [3, 4], [5, 6]]. Deze keer wil je een functie maken die recursie gebruikt om alle lagen van de lijst diep te kopiëren. In plaats van het vorige lijstbegrip:

[x for x in _list]

Het gebruikt een nieuwe voor lijsten:

[deepcopy_list(x) for x in _list]

En deepcopy_listziet er zo uit:

def deepcopy_list(x):
  if isinstance(x, (str, bool, float, int)):
    return x
  else:
    return [deepcopy_list(y) for y in x]

Dan heb je nu een functie die elke lijst van strs, bools, floast, intsen zelfs lijstenkan deepcopy naar oneindig veel lagen met behulp van recursie. En daar heb je het, deepcopying.

TLDR: Deepcopy gebruikt recursie om objecten te dupliceren en retourneert alleen dezelfde onveranderlijke objecten als voorheen, aangezien onveranderlijke objecten niet kunnen worden gedupliceerd. Het kopieert echter de meest binnenste lagen van veranderlijke objecten totdat het de buitenste veranderlijke laag van een object bereikt.


Antwoord 14

Een klein praktisch perspectief om in het geheugen te kijken via id en 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) 

15

De deepcopy-optie is de enige methode die voor mij werkt:

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('-----------------------------')

leidt tot uitvoer van:

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

Antwoord 16

Dit komt doordat de regel new_list = my_listeen nieuwe verwijzing toewijst aan de variabele my_listdie new_listis
Dit is vergelijkbaar met de onderstaande C-code,

int my_list[] = [1,2,3,4];
int *new_list;
new_list = my_list;

U moet de kopieermodule gebruiken om een nieuwe lijst te maken door

import copy
new_list = copy.deepcopy(my_list)

Antwoord 17

De te gebruiken methode hangt af van de inhoud van de lijst die wordt gekopieerd. Als de lijst geneste dictsbevat, dan is deepcopy de enige methode die werkt. en voer het in dezelfde tijd uit (behalve voor lus en deepcopy, die het slechtst presteerden).

Script

from random import randint
from time import time
import copy
item_count = 100000
def copy_type(l1: list, l2: list):
  if l1 == l2:
    return 'shallow'
  return 'deep'
def run_time(start, end):
  run = end - start
  return int(run * 1000000)
def list_combine(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = [] + l1
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'combine', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}
def list_extend(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = []
  l2.extend(l1)
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'extend', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}
def list_unpack(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = [*l1]
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'unpack', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}
def list_deepcopy(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = copy.deepcopy(l1)
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'deepcopy', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}
def list_copy(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = list.copy(l1)
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'copy', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}
def list_slice(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = l1[:]
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'slice', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}
def list_loop(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = []
  for i in range(len(l1)):
    l2.append(l1[i])
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'loop', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}
def list_list(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = list(l1)
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'list()', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}
if __name__ == '__main__':
  list_type = [{'list[dict]': {'test': [1, 1]}}, 
          {'list[list]': [1, 1]}]
  store = []
  for data in list_type:
    key = list(data.keys())[0]
    store.append({key: [list_unpack(data[key]), list_extend(data[key]), 
                list_combine(data[key]), list_deepcopy(data[key]), 
                list_copy(data[key]), list_slice(data[key]),           
                list_loop(data[key])]})
  print(store)

Resultaten

[{"list[dict]": [
  {"method": "unpack", "copy_type": "shallow", "time_µs": 56149},
  {"method": "extend", "copy_type": "shallow", "time_µs": 52991},
  {"method": "combine", "copy_type": "shallow", "time_µs": 53726},
  {"method": "deepcopy", "copy_type": "deep", "time_µs": 2702616},
  {"method": "copy", "copy_type": "shallow", "time_µs": 52204},
  {"method": "slice", "copy_type": "shallow", "time_µs": 52223},
  {"method": "loop", "copy_type": "shallow", "time_µs": 836928}]},
{"list[list]": [
  {"method": "unpack", "copy_type": "deep", "time_µs": 52313},
  {"method": "extend", "copy_type": "deep", "time_µs": 52550},
  {"method": "combine", "copy_type": "deep", "time_µs": 53203},
  {"method": "deepcopy", "copy_type": "deep", "time_µs": 2608560},
  {"method": "copy", "copy_type": "deep", "time_µs": 53210},
  {"method": "slice", "copy_type": "deep", "time_µs": 52937},
  {"method": "loop", "copy_type": "deep", "time_µs": 834774}
]}]

Antwoord 18

Er is een eenvoudige techniek om dit aan te pakken.

Code:

number=[1,2,3,4,5,6] #Original list
another=[] #another empty list
for a in number: #here I am declaring variable (a) as an item in the list (number)
    another.append(a) #here we are adding the items of list (number) to list (another)
print(another)

Uitvoer:

>>> [1,2,3,4,5,6]

Ik hoop dat dit nuttig was voor uw vraag.


Antwoord 19

Er is een andere manier om een lijst te kopiëren die tot nu toe niet in de lijst stond: een lege lijst toevoegen: l2 = l + [].

Ik heb het getest met Python 3.8:

l = [1,2,3]
l2 = l + []
print(l,l2)
l[0] = 'a'
print(l,l2)

Het is niet het beste antwoord, maar het werkt.

Other episodes