5

I am a beginner at python and I don't want these arrays to copy each other but they are automatically:

a = numpy.zeros(4)
b = a
a[1] = 10
print b[1]

and it returns 10 instead of 0. How do I disconnect these two arrays?

Matt
  • 724
  • 4
  • 11
Mark Aisenberg
  • 123
  • 1
  • 2
  • 4
  • 5
    Assignment does not create a copy. To understand why your expectation was wrong see [Facts and myths about Python names and values](http://nedbatchelder.com/text/names.html) by Ned Batchelder. It's important to read a thorough treatment of this if you're really going to understand Python. – Steven Rumbalski Apr 21 '15 at 16:01

4 Answers4

4

"The arrays are automatically copying each other" is a false statement for several reasons. The primary reason is that you only have one array, and two variables names that refer to that array.

Here are three ways to copy a numpy array (i.e. create another array exactly like it):

>>> a = numpy.zeros(4)
>>> b = a.copy()
>>> c = numpy.copy(a)
>>> d = numpy.array(a)
>>> a[1] = 10
>>> a
array([  0.,  10.,   0.,   0.])
>>> b
array([ 0.,  0.,  0.,  0.])
>>> c
array([ 0.,  0.,  0.,  0.])
>>> d
array([ 0.,  0.,  0.,  0.])

Note that slice-copying (e.g. e = a[:]) will not work with numpy arrays.

Shashank
  • 13,713
  • 5
  • 37
  • 63
  • "creates another array which is a deep-copy of the first" is a bit misleading: what the slice operation does is create another array (i.e., a new Python object of type `numpy.ndarray`) that *shares* the same data block as the original array. There's no copying of array *data* going on, deep or otherwise. – Mark Dickinson Apr 21 '15 at 16:49
  • @MarkDickinson It is a deep-copy though. Try `a = numpy.zeros(4);b=a[:];a[0] is b[0]` and you will see that it gives you False I was surprised as well by this behavior. It's definitely different from lists. However, even if it is a deep-copy in Python, what you essentially have is two different objects that point to the same address in memory. In assignment, the value at that address is modified so that's why you see the change across objects. – Shashank Apr 21 '15 at 16:51
  • 2
    That's because when you pull out `a[0]` or `b[0]`, you're effectively boxing the unboxed value held in the array, and creating a new Python object at that point. Try `a[0] is a[0]` and you'll also see `False`. Then try modifying `a`: you'll see that `b`'s contents also change (and vice versa). – Mark Dickinson Apr 21 '15 at 16:53
  • @MarkDickinson Ah interesting, the concept of "unboxing" makes sense, and the `a[0] is a[0]` being False is definitely interesting...I'll update the answer to remove the false information. – Shashank Apr 21 '15 at 16:54
2

You need a copy:

b  = a.copy()

b = a creates a reference so a is b, they are both pointing to the same location in memory, a.copy() actually creates a new object.

In [5]: a = numpy.zeros(4)    
In [6]: b = a   # reference
In [7]: id(a)
Out[7]: 140335847505968    
In [8]: id(b)         # same id's 
Out[8]: 140335847505968    
In [9]: a is b
Out[9]: True    
In [10]: b = a.copy() # new object    
In [11]: id(a)
Out[11]: 140335847505968    
In [12]: id(b)   # # now  different id's
Out[12]: 140335437696176    
In [13]: a is b # b is no longer pointing to the same memory location
Out[13]: False

If you slice the array using basic slicing, the id's will differ but any changes will be reflected in both a and b as when using basic indexing All arrays generated by basic slicing are always views of the original array. A view is An array that does not own its data, but refers to another array’s data instead. So the view is a new object but the content still belongs to the original array.

However using advanced indexing Advanced indexing always returns a copy of the data

In [141]: a = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])   
In [142]: b = a[1:7:2]    # basic indexing/view
In [143]: id(a)
Out[143]: 140335437385856    
In [144]: id(b)      
Out[144]: 140335437356528    
In [145]: b[0] = 999    
In [146]: a
Out[146]: array([  0, 999,   2,   3,   4,   5,   6,   7,   8,   9])
In [148]: b
Out[148]: array([999,   3,   5])    
In [149]: a = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])    
In [150]: b = a[[0,3,5]]  # advanced indexing/copy  
In [151]: b
Out[151]: array([0, 3, 5])    
In [152]: b[0] = 999    
In [153]: a
Out[153]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])    
In [154]: b
Out[154]: array([999,   3,   5])
In [157]: a = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [158]: b = a[a]   # copy
In [159]: b
Out[159]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])    
In [160]: b[0] = 99    
In [161]: a
Out[161]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])    
In [162]: b
Out[162]: array([99,  1,  2,  3,  4,  5,  6,  7,  8,  9])

This is specific numpy behaviour, slicing a regular python flat list will always create a new list where changes in a will not be reflected in b.

In [190]: a = [1,2,3,4,5]

In [191]: b = a[:3]

In [192]: b[0] = 999

In [193]: a
Out[193]: [1, 2, 3, 4, 5]

In [194]: b
Out[194]: [999, 2, 3]

Where you will get caught with a python list is if the list contains sublists and you create a shallow copy:

In [197]: a = [[1,2,3],[4,5]]
In [198]: b = a[:]    
In [199]: id(a)
Out[199]: 140335437468296    
In [200]: id(b)
Out[200]: 140335437417992
In [201]: b[0][0] = 999 
In [202]: b
Out[202]: [[999, 2, 3], [4, 5]]   
In [203]: a
Out[203]: [[999, 2, 3], [4, 5]]

You would need to make a copy.deepcopy:

In [204]: a = [[1,2,3],[4,5]]    
In [205]: from copy import  deepcopy
In [206]: b = deepcopy(a)    
In [207]: b[0][0] = 999    
In [208]: b
Out[208]: [[999, 2, 3], [4, 5]]    
In [209]: a
Out[209]: [[1, 2, 3], [4, 5]]
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • It should be noted that `a.copy()` isn't valid Python 2, which the OP uses. – erb Apr 21 '15 at 16:03
  • 3
    @erb, it works in python2 also, the OP has a numpy array not a list. – Padraic Cunningham Apr 21 '15 at 16:04
  • Interestingly enough, although `id(a) == id(a[:])` evaluates to `False`, slicing does not create a copy. `a[:][3] = 4` will mutate `a`. (You made no such claim about slicing, but you did use `id` to show they are the same. Here is a case where `id` gave an incomplete picture.) – Steven Rumbalski Apr 21 '15 at 16:21
-1

Remember lists in Python are mutable, which means when you do the assignation operation, it does not create a copy of it, but instead, copies the reference. Which means both variables are the same list.

You can do for example:

b = a[::] which creates a copy of the original list.

Anyway, I'd recommend you read Python.org lists section for more information.

fedest
  • 1,190
  • 3
  • 15
  • 35
  • 3
    Assignment works the same way regardless of whether the value being assigned is immutable. I downvoted this answer because it adds unnecessary confusion to what should be a simpler explanation. Also, `b = a[::]` is more commonly written `b = a[:]`. – Steven Rumbalski Apr 21 '15 at 16:08
-1

You can use the copy module like this

from copy import copy
a = numpy.zeros(4)
b = copy(a)
a[1] = 10
print b[1]

This has to do with the fact that when you do b = a you assign the reference of a to b.

More on this can be found in this answer: How to clone or copy a list?

Community
  • 1
  • 1
erb
  • 14,503
  • 5
  • 30
  • 38
  • 3
    This is good general purpose knowledge about copying, but in the specific case of numpy arrays you can count on the array having a `copy` method. – Steven Rumbalski Apr 21 '15 at 16:05