2

I have the following function:

def a_function(foo):
    bar = foo
    print("bar1: ", bar, id(foo))
    bar[0] = foo[2]
    print("bar2: ", bar, id(foo))

Call with list as parameter:

foo = [0, 1, 2, 3, 4, 5, 6, 7, 8]
print("foo1: ", foo, id(foo))
a_function(foo[:])
print("foo2: ", foo, id(foo))

Output:

foo1:  [0, 1, 2, 3, 4, 5, 6, 7, 8] 140118901565768
bar1:  [0, 1, 2, 3, 4, 5, 6, 7, 8] 140118901566472
bar2:  [2, 1, 2, 3, 4, 5, 6, 7, 8] 140118901566472
foo2:  [0, 1, 2, 3, 4, 5, 6, 7, 8] 140118901565768

Call with ndarray as parameter:

foo = np.arange(0,9)
print("foo1: ", foo, id(foo))
a_function(foo[:])
print("foo2: ", foo, id(foo))

Output:

foo1:  [0 1 2 3 4 5 6 7 8] 139814139381760
bar1:  [0 1 2 3 4 5 6 7 8] 139814115258000
bar2:  [2 1 2 3 4 5 6 7 8] 139814115258000
foo2:  [2 1 2 3 4 5 6 7 8] 139814139381760

I passed it as a copy foo[:] even copied again inside the function and also it has it's own id. However foo2 changed it's value outside the scope of the function when it's a ndarray. How is this possible?

Agent49
  • 133
  • 1
  • 11
  • 1
    Read the NumPy answer in the above duplicate. It basically makes a shallow copy of the array. Try using `foo.copy()`. – rayryeng Mar 29 '17 at 16:14
  • 1
    @rayryeng: `[:]` for a Python list creates a shallow copy. For a numpy array, `[:]` doesn't copy, it creates just another view into the array. – jfs Mar 30 '17 at 14:08
  • @J.F.Sebastian Right. I'm mixing up my terminology. Thanks for the clarification. – rayryeng Mar 30 '17 at 14:11
  • @rayryeng: note: the difference is not just words. [Modifying a copy may keep the original list intact but modifying an array view changes the original.](https://gist.github.com/zed/c94d974a3b7048b45bd4fb3aa0a77a39) – jfs Mar 30 '17 at 14:34

2 Answers2

2

In Python,

bar = foo

does not make a copy. bar and foo reference the same array object.

This action makes a copy of a list, but just a view of an array:

foo[:]

You need to use foo.copy() if you want to isolate changes in bar from foo.

I'd suggest reviewing some of the basic numpy documentation, especially the stuff about views and copies.

hpaulj
  • 221,503
  • 14
  • 230
  • 353
2

First of all, you don't pass "by value" by passing a full slice of a sequence, Python always passes by assignment, i.e. the first thing that implicitly happens is foo = argument when calling your function.

Now for your actual question:

Creating a full slice of a list creates a new list. You are passing a shallow copy of your original list to the function.

Creating a (full) slice of a numpy array produces a view onto the same data (somewhere in memory) the original array holds. The view and the original share the same data in the sense that if you change the content of one the change is visible across all views.

Further reading: http://scipy-cookbook.readthedocs.io/items/ViewsVsCopies.html

timgeb
  • 76,762
  • 20
  • 123
  • 145
  • I know that Python passes by assignment. I found this very helpful: [http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference](http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference). I just saw that I can implicitly pass it by value by copying the sequence "foo[:]" which works for lists. But you're right anyways. I will have to read about views in the numpy documentation. Thank you. – Agent49 Mar 29 '17 at 16:28