0

when I just have a regular list and want to copy this list without changes happening to the other:

sent1=['The', 'dog', 'gave', 'John', 'the', 'newspaper']
sent2 = sent1[:]
sent1[1] = 'monkey'
sent2 

['The', 'dog', 'gave', 'John', 'the', 'newspaper']

We can see sent2 is unchanged. However, when I have a nested list such as

text1=[['The', 'dog', 'gave', 'John', 'the', 'newspaper'], ['John', 'is', 'happy']]
text2 = text1[:]
text1[0][1] = 'monkey'
text2

[['The', 'monkey', 'gave', 'John', 'the', 'newspaper'], ['John', 'is', 'happy']]

We see that sent2 IS changed. Can someone explain why this happens in a nested list?

`

Jason Lee
  • 9
  • 1
  • 1
    It's the same thing as when you don't use a slice on a flat list and assign it to another variable. Whenever you don't slice a list or otherwise make a copy, you're dealing with an alias. – ggorlen Sep 27 '20 at 19:25
  • You can try: text2 = copy.deepcopy(text1) to get the effect you are looking for. – quamrana Sep 27 '20 at 19:30

1 Answers1

0

When you copy a list via [:] it performs a shallow copy. This means each element of the list remains the same in the copied version. For that reason the sublists are not copied themselves, but instead the outer list retains its references to them. If you want to copy the sublists as well, you can use copy.deepcopy.

a_guest
  • 34,165
  • 12
  • 64
  • 118
  • 1
    Keep in mind `copy.deepcopy` is about [20x slower](https://stackoverflow.com/a/62865237/6243352) than `[x[:] for x in lst]`. I only advise using it if the objects [implement `__deepcopy__`](https://stackoverflow.com/a/15774013/6243352) and/or you have extremely complex nesting, or you don't care at all about performance or it's not the bottleneck. – ggorlen Sep 27 '20 at 19:29
  • @ggorlen Code should be expressive and clear and I think `copy.deepcopy` fulfills these goals more than this list comprehension. Performance can play a role, but shouldn't guide code design right from the beginning. Only if monitoring and measurements reveal certain bottlenecks, they may be addressed. – a_guest Sep 27 '20 at 19:33
  • 1
    I agree--premature optimization is the root of all evil. In this case, though, I don't know of anything much more idiomatic than a LC and a slice. Every person coding Python for any non-trivial period of time will understand the intent behind the above LC just as easily as `deepcopy`. DC behaves [more surprisingly under the hood](https://github.com/python/cpython/blob/master/Lib/copy.py#L128) than some may guess, and the performance is so awful that it's worth making mention of. It's a last resort and major overkill for copying a simple 2d list of strings. Extra import too. – ggorlen Sep 27 '20 at 19:37
  • 1
    In fact, with the LC, the code is more precise and clear as to intent--it's saying "copy every element in the list", so it's explicitly targeting a 2d structure. On the other hand, `copy.deepcopy` says "I have no idea _what_ this thing is or how deep it is, just copy it all, whatever". If you like the language of "copy" (again dubious since `[:]` is quite universally understood as a list copy), then `[x.copy() for x in lst]` gives the best of both worlds, although I think you lose some type precision because `copy` works on other objects than lists. – ggorlen Sep 27 '20 at 19:45