38

I occasionally see the list slice syntax used in Python code like this:

newList = oldList[:]

Surely this is just the same as:

newList = oldList

Or am I missing something?

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
Charles Anderson
  • 19,321
  • 13
  • 57
  • 73

5 Answers5

53

[:] Shallow copies the list, making a copy of the list structure containing references to the original list members. This means that operations on the copy do not affect the structure of the original. However, if you do something to the list members, both lists still refer to them, so the updates will show up if the members are accessed through the original.

A Deep Copy would make copies of all the list members as well.

The code snippet below shows a shallow copy in action.

# ================================================================
# === ShallowCopy.py =============================================
# ================================================================
#
class Foo:
    def __init__(self, data):
        self._data = data

aa = Foo ('aaa')
bb = Foo ('bbb')

# The initial list has two elements containing 'aaa' and 'bbb'
OldList = [aa,bb]
print OldList[0]._data

# The shallow copy makes a new list pointing to the old elements
NewList = OldList[:]
print NewList[0]._data

# Updating one of the elements through the new list sees the
# change reflected when you access that element through the
# old list.
NewList[0]._data = 'xxx'
print OldList[0]._data

# Updating the new list to point to something new is not reflected
# in the old list.
NewList[0] = Foo ('ccc')
print NewList[0]._data
print OldList[0]._data

Running it in a python shell gives the following transcript. We can see the list being made with copies of the old objects. One of the objects can have its state updated by reference through the old list, and the updates can be seen when the object is accessed through the old list. Finally, changing a reference in the new list can be seen to not reflect in the old list, as the new list is now referring to a different object.

>>> # ================================================================
... # === ShallowCopy.py =============================================
... # ================================================================
... #
... class Foo:
...     def __init__(self, data):
...         self._data = data
...
>>> aa = Foo ('aaa')
>>> bb = Foo ('bbb')
>>>
>>> # The initial list has two elements containing 'aaa' and 'bbb'
... OldList = [aa,bb]
>>> print OldList[0]._data
aaa
>>>
>>> # The shallow copy makes a new list pointing to the old elements
... NewList = OldList[:]
>>> print NewList[0]._data
aaa
>>>
>>> # Updating one of the elements through the new list sees the
... # change reflected when you access that element through the
... # old list.
... NewList[0]._data = 'xxx'
>>> print OldList[0]._data
xxx
>>>
>>> # Updating the new list to point to something new is not reflected
... # in the old list.
... NewList[0] = Foo ('ccc')
>>> print NewList[0]._data
ccc
>>> print OldList[0]._data
xxx
ConcernedOfTunbridgeWells
  • 64,444
  • 15
  • 143
  • 197
  • 5
    This is the better, more complete answer. – Brian C. Lane Nov 27 '08 at 16:40
  • 1
    BTW I don't like the 'shallow/deep' distinction. It doesn't really capture the complexity of the issue. Should a deep copy do a shallow-copy or a deep-copy of its members? In practice when you need to copy a complex object graph, you usually want to clone some object types while only copy references to others. – Kos Jan 06 '13 at 08:40
  • @Kos a deep copy is always *recursively deep* - if you want a copy that is deep up to a point then becomes shallow, you would have to implement that yourself (and I don't know what an appropriate term would be - "sand bar" copy?!) – jonrsharpe May 14 '15 at 10:18
51

Like NXC said, Python variable names actually point to an object, and not a specific spot in memory.

newList = oldList would create two different variables that point to the same object, therefore, changing oldList would also change newList.

However, when you do newList = oldList[:], it "slices" the list, and creates a new list. The default values for [:] are 0 and the end of the list, so it copies everything. Therefore, it creates a new list with all the data contained in the first one, but both can be altered without changing the other.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
Deinumite
  • 3,979
  • 3
  • 25
  • 22
  • 7
    As mentioned in other answers, this is called a "shallow copy". – Mike Mazur Jul 23 '09 at 10:10
  • Then why "Assignment to slices is also possible, and this can even change the size of the list or clear it entirely"? See link(https://docs.python.org/3.4/tutorial/introduction.html) – h9uest May 13 '15 at 14:59
  • *"both can be altered without changing the other"* - this is **only true** because the list in question contains *immutable objects*. If it contained *mutable objects* then changes to the objects in `newList` would be reflected in `oldList`, and vice versa - this is an important distinction. – jonrsharpe May 14 '15 at 10:14
  • @jonrsharpe He didn't say *every* change of one won't change the other. Even with mutable objects in the lists, you can for example extend one list and that won't change the other. I think he meant the two list objects themselves, not their contents. – Stefan Pochmann May 14 '15 at 10:24
  • @StefanPochmann yes, but [evidently](http://stackoverflow.com/q/30220736/3001761) it's not completely clear, and I think it's a distinction worth making explicit. – jonrsharpe May 14 '15 at 10:35
12

As it has already been answered, I'll simply add a simple demonstration:

>>> a = [1, 2, 3, 4]
>>> b = a
>>> c = a[:]
>>> b[2] = 10
>>> c[3] = 20
>>> a
[1, 2, 10, 4]
>>> b
[1, 2, 10, 4]
>>> c
[1, 2, 3, 20]
4

Never think that 'a = b' in Python means 'copy b to a'. If there are variables on both sides, you can't really know that. Instead, think of it as 'give b the additional name a'.

If b is an immutable object (like a number, tuple or a string), then yes, the effect is that you get a copy. But that's because when you deal with immutables (which maybe should have been called read only, unchangeable or WORM) you always get a copy, by definition.

If b is a mutable, you always have to do something extra to be sure you have a true copy. Always. With lists, it's as simple as a slice: a = b[:].

Mutability is also the reason that this:

def myfunction(mylist=[]): 
    pass

... doesn't quite do what you think it does.

If you're from a C-background: what's left of the '=' is a pointer, always. All variables are pointers, always. If you put variables in a list: a = [b, c], you've put pointers to the values pointed to by b and c in a list pointed to by a. If you then set a[0] = d, the pointer in position 0 is now pointing to whatever d points to.

See also the copy-module: http://docs.python.org/library/copy.html

kaleissin
  • 1,245
  • 13
  • 19
  • 2
    I think it's misleading to say that for immutable objects you 'always get a copy', as it's very unlikely that any copying will happen. The point is that letting an immutable object have more than one name is as good as a copy, as it can't change its value. It's actually quite difficult to make a real copy of immutable types like strings (for example). – Scott Griffiths Mar 04 '10 at 17:43
-2

Shallow Copy: (copies chunks of memory from one location to another)

a = ['one','two','three']

b = a[:]

b[1] = 2

print id(a), a #Output: 1077248300 ['one', 'two', 'three']
print id(b), b #Output: 1077248908 ['one', 2, 'three']

Deep Copy: (Copies object reference)

a = ['one','two','three']

b = a

b[1] = 2


print id(a), a #Output: 1077248300 ['one', 2, 'three']
print id(b), b #Output: 1077248300 ['one', 2, 'three']
abhiomkar
  • 4,548
  • 7
  • 29
  • 24
  • 8
    That's not a Deep Copy - a deep copy is a recursive copy (http://docs.python.org/library/copy.html). In a shallow copy of a list, sublists of that list won't be copied, only re-referenced. A deep copy recursively descends into sublists and subdicts and really copies all of their entries. – Tim Pietzcker May 20 '10 at 08:50