71

I want to copy a 2D list, so that if I modify one list, the other is not modified.

For a one-dimensional list, I just do this:

a = [1, 2]
b = a[:]

And now if I modify b, a is not modified.

But this doesn't work for a two-dimensional list:

a = [[1, 2],[3, 4]]
b = a[:]

If I modify b, a gets modified as well.

How do I fix this?

vaultah
  • 44,105
  • 12
  • 114
  • 143
SuperString
  • 21,593
  • 37
  • 91
  • 122
  • 7
    A whole lot of the time when people user nested lists and need to copy them in this way, they really want to be using `numpy`. – Mike Graham Mar 29 '10 at 23:23
  • 1
    imho, that's just a bug in the language. behavior that's different for two cases where it should be identical - typical for interpreted languages. if you have large code, very difficult to debug – Serhii Apr 02 '19 at 19:38
  • @SerhiiPoklonskyi No, it's not a bug. When you do `b = a[:]`, you create a new list `b`, so for example `a.append([5, 6])` will not modify `b`, as it just changes `a`. However, the line `a[1][0] = 5` **will** change `b` because it changes a list that `b` refers to. – Artemis Apr 09 '19 at 01:27
  • @ArtemisFowl doesn't work for me (a[1][0] does not modify b as well). Even if it would, I don't understand how that is relevant. The problem is: when you do `a = b.copy()`, `a` becomes a separate place in memory: neither reference nor pointer to `b`, i.e. it's an independent variable. However, if you do `a = b.copy()` and `b` is an array, that does not work. how may there be any logical explanation for that? if `b` is an array, `a = b.copy()` MUST create an independent variable. otherwise it's a bug. p.s. no intention to be rude, pls explain to me if I am wrong – Serhii Apr 09 '19 at 05:57
  • @SerhiiPoklonskyi Not sure I entirely understand? – Artemis Apr 09 '19 at 10:44
  • @ArtemisFowl let me try better. imagine: `a` is 1-dimensional array. imagine you do `b = a.copy()`. what happens? `b` is a copy of `a` but independent from it. if you change `a`, nothing happens to `b`. it has its own memory cell. now imagine `a` is 2-dimensional array. imagine you do 'b = a.copy()'. `b` is a copy of `a` but if you cange `a` now, `b` is changed as well!. Don't you find this inconsistent and wrong behaviour? – Serhii Apr 10 '19 at 18:08
  • @ArtemisFowl regardless, just look at the question. It's clear from it that a command that yields result 'x' in one circumstance, yields result 'y' in other circumstances. imho, there can be no valid explanation for that. I mean, coding can be difficult by itself, language must be clear and consistent and not make it even more difficult – Serhii Apr 10 '19 at 18:19
  • 4
    @SerhiiPoklonskyi I think the reason that you find this confusing is that you miss-understand Python. Python does not actually have '2-dimensional arrays' as such, it just has lists, which can contain other lists. I will try to demonstrate by means of an example: you define `a` with `a = [[1, 2], [3, 4]]`. Then you create a copy of `a`: `b = a.copy`. This is a different list, but it contains the same 'sub-lists' this means that changing `b`, for example `b.append([5, 6])` will not change `a`, however changing a list in `b`, for example `b[0].append(3)` will also change the first list of `a`. – Artemis Apr 10 '19 at 18:34
  • @ArtemisFowl ...because the sublists are not deep copies, only the main list is? so modifying the main list does not change its copy, but modifying sublists does? in other words, to solve it, I could do `a = [i.copy() for i in b]` do I understand correctly? – Serhii Apr 10 '19 at 18:39
  • @ArtemisFowl yes, works for me now. Ok, then it's not a bug , just a lack of understanding. I actually think, your explanation must be a real answer to the question. thank you for taking time! – Serhii Apr 10 '19 at 18:41
  • @SerhiiPoklonskyi You're welcome. – Artemis Apr 10 '19 at 18:45
  • Does this answer your question? [How to clone or copy a list?](https://stackoverflow.com/questions/2612802/how-to-clone-or-copy-a-list) – AMC Jan 25 '20 at 22:05

4 Answers4

90

For a more general solution that works regardless of the number of dimensions, use copy.deepcopy():

import copy
b = copy.deepcopy(a)
Ayman Hourieh
  • 132,184
  • 23
  • 144
  • 116
  • Though in most cases, I'd probably say `from copy import deepcopy` since a name conflict is unlikely, and it looks nicer. ;) – Amber Mar 29 '10 at 23:18
  • 2
    @Dav, you make a valid point. I prefer to always import modules in order to avoid name conflicts instead of handling functions on a case-by-case basis. :) – Ayman Hourieh Mar 29 '10 at 23:20
  • 3
    Note that this will also deepcopy the actual elements in the lists. – FogleBird Mar 29 '10 at 23:27
  • 1
    @Dav, I disagree, it's generally better to use the module.function() format. – FogleBird Mar 29 '10 at 23:28
  • 5
    "Namespaces are one honking great idea -- let's do more of those!" –  Mar 29 '10 at 23:37
  • @FogleBird: Personal preference. – Amber Mar 30 '10 at 00:40
  • 1
    @FogleBird: However, PEP-8 does actually seem to imply that `from ... import ...` is the norm unless there are namespace conflicts: http://www.python.org/dev/peps/pep-0008/ (see "Imports"). – Amber Mar 30 '10 at 01:32
  • 1
    I don't see anything there which really applies here - mostly just a statement of opinion without support, and then a mention of potential circular or delayed import issues, which don't apply to the standard library nor, actually, most modules. In fact, the circular import issues section even explicitly notes that the problem doesn't require `from .. import` usage to manifest. – Amber Mar 30 '10 at 03:34
85
b = [x[:] for x in a]
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • 9
    +1 since appropriate. I personally like avoiding copy / deepcopy (very very rarely had a valid use case for them in real life ; the same can be said for a list with more then 2 dimensions imo) – ChristopheD Mar 29 '10 at 23:16
  • 1
    Can you please provide a use case? I'm trying to copy a 2D list, but I'm not sure what to replace with the variable names you provide. – John Locke Jan 10 '19 at 01:18
  • @JohnLocke `b` is the new list, `a` is the old one. `x` is used internally. – Artemis Apr 09 '19 at 01:29
  • for me, `path` was a 2D array and I wanted to copy `path[i]` so I did `curr_level = [x[:] for x in path[i]]` – mLstudent33 Jan 04 '20 at 20:47
  • This is what I call efficient (and clever) programming! (Importing **extra** modules for something that can be done simply w/o them is inefficient.) – Apostolos Jan 08 '21 at 18:05
5

Whis b = a[:] doesn't work for nested list(or say muti-dimension list)?

a = [[1, 2],[3, 4]]
b = a[:]

Answer: Though when we are copying the list a using slicing[:] operation but the inner sub-list still refers to the inner-sub list of list b

Note: We can check the reference using id() in python.
Let's understand using an example.

>>> a = [[1,2],[3,4]]
>>> id(a)
140191407209856    # unique id of a
>>> b=a
>>> id(b)
140191407209856
>>> b=a[:]        # Copying list a to b with slicing
>>> id(b)         
140191407209920   # id of list b changed & is not same as id of list a
>>> id(a[0])      
140191407188544
>>> id(b[0])
140191407188544
>>> id(a[0])==id(b[0])  # id of both a[0] & b[1] is same.
True

So, slicing won't change the reference for objects inside the list. You can notice from above that reference of a[0] is the same as b[0].
When you copy a 2D list to another, it adds a reference to it not the actual list.

Instead you can use:

  • b = copy.deepcopy(a)
  • b = [item[:] for item in a]
  • b = [item.copy() for item in a]
  • b = [list(item) for item in a]
  • b = [copy.copy(item) for item in a]
  • b = []; b.extens[a]

Below is the comparison of the time complexity of all available copy methods (source)

  1. 10.59 sec (105.9us/itn) - copy.deepcopy(old_list)

  2. 10.16 sec (101.6us/itn) - pure python Copy() method copying classes with deepcopy

  3. 1.488 sec (14.88us/itn) - pure python Copy() method not copying classes (only dicts/lists/tuples)

  4. 0.325 sec (3.25us/itn) - for item in old_list: new_list.append(item)

  5. 0.217 sec (2.17us/itn) - [i for i in old_list] (a list comprehension)

  6. 0.186 sec (1.86us/itn) - copy.copy(old_list)

  7. 0.075 sec (0.75us/itn) - list(old_list)

  8. 0.053 sec (0.53us/itn) - new_list = []; new_list.extend(old_list)

  9. 0.039 sec (0.39us/itn) - old_list[:] (list slicing)

Tushar
  • 880
  • 9
  • 17
  • `b = []; b.extend[a]` won't work here - it's a shallow copy, like `b = [item for item in a]` would be. – slothrop May 26 '23 at 10:43
0

You can use the following for two-dimensional nested arrays: b = [[y for y in x] for x in a]