1

I have a function that takes a list or numpy array v. The function modify v so also the original vector x changes (because of curse this is a reference). I would like to have the old value of x. There are some easy solutions to this but they do not look very elegant.

What is the most pythonic way to solvethis problem?

Let consider for example

def change_2(v):
    v[2]=6
    return v 

x=[1,2,3]
z=change_2(x)
print "x=", x,"z=",z
x= [1, 2, 6] z= [1, 2, 6] #I would like x=[1, 2, 3]
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
Donbeo
  • 17,067
  • 37
  • 114
  • 188

3 Answers3

3

Create a copy of the list:

def change_2(v):
    v = v[:]
    v[2] = 6
    return v

Python is already giving you the value, the object itself. You are altering that value by changing its contents.

v[:] takes a slice of all indices in the list, creating a new list object to hold these indices. An alternative way would be to use the list() constructor:

v = list(v)

Another alternative is to leave the responsibility of creating a copy up to the caller:

def change_2(v):
    v[2] = 6
    return v 

x = [1,2,3]
z = change_2(x[:])

By creating a full copy first, you now have a different value.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • this is the "not elegant" solution I was thinking about. Is this the most pythonic way? Is there anything like def change_2(v*)? – Donbeo Dec 21 '13 at 14:26
  • 3
    @Donbeo: No, you misunderstand what is being passed. Python deals exclusively with references to objects, each and every name, plus container contents and attributes, is a reference to an object, and the objects represent the values. – Martijn Pieters Dec 21 '13 at 14:28
  • 2
    @Donbeo: It is the *value* you are changing. The `list` object, which is a sequence of references itself. You are changing a reference inside that list object. If you did not want to do that, then you have to create a copy yourself. – Martijn Pieters Dec 21 '13 at 14:29
2

Everything in Python is reference. If you need an immutable argument, pass a copy.

A copy can easily be created as v[:] or as list(v)

So your call becomes

z=change_2(x[:])

The first notation, uses the slice notation to create a copy of the list. In case, your list have mutable references within, you would need deepcopy, which can be achieved through copy.deepcopy

For Numpy arrays as @delnan has commented, slice notation returns a view rather than a copy. To get over it, for numpy arrays use the copy method

>>> def foo(v):
    v[0] = 1


>>> v = np.zeros(5)
>>> foo(v.copy())
>>> v
array([ 0.,  0.,  0.,  0.,  0.])
Abhijit
  • 62,056
  • 18
  • 131
  • 204
  • Technically all lists have references within (though sometimes it doesn't matter because the referenced objects are immutable, such as ints). And for numpy arrays, slicing returns a view IIRC. –  Dec 21 '13 at 14:27
  • @delnan: Yes you are true technically and cognitively – Abhijit Dec 21 '13 at 14:34
1

Just create a copy of the list, modify and return the copy

def change_2(v):
    w = v[:]      # Create a copy of v
    w[2] = 6      # Modify the copy
    return w      # Return the copy
thefourtheye
  • 233,700
  • 52
  • 457
  • 497