2

This code:

def do_something(arg):
    x=arg
    x[0] = 9
    return x

y = [1,2]

print "before: ", y
print "result of do_something(): ", do_something(y)
print "after: ", y

Results in this:

before:  [1, 2]
result of do_something():  [9, 2]
after:  [9, 2]

I dont get why the function changes the original lists that I pass? I even (try to) create a new copy of arg (x) to prevent it. Even if I dont return x, I still get the same result.

wessman
  • 92
  • 7
  • 3
    Possible duplicate of ["Least Astonishment" in Python: The Mutable Default Argument](http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument) or [How to clone or copy a list in Python?](http://stackoverflow.com/questions/2612802/how-to-clone-or-copy-a-list-in-python) – styvane Nov 14 '15 at 10:57
  • 1
    watch [Facts and Myth about Python names and values](https://www.youtube.com/watch?v=_AEJHKGk9ns) by Ned Batchelder and all will be clear – miraculixx Nov 14 '15 at 11:01
  • If you're using Python3 and got here via Google, the solution you want is `list.copy()` on the input: https://www.afternerd.com/blog/python-copy-list/ – LShaver Jul 08 '23 at 20:00

4 Answers4

2

Because list is passed as link to the original object. You need to copy it in your function or to pass to it new object of the list.
You could do it like that:

def do_something(arg):
    x=arg[:]
    x[0] = 9
    return x

y = [1,2]

print "before: ", y
print "result of do_something(): ", do_something(y)
print "after: ", y

Results:

before:  [1, 2]
result of do_something():  [9, 2]
after:  [1, 2]
Anton Protopopov
  • 30,354
  • 12
  • 88
  • 93
1

All parameters (arguments) in the Python language are passed by reference. So, you pass a reference to a list object. And in function you make changes in the list. So, that's why you see changed list after calling the function.

To avoid - copy list/create a new one, work with it and return it as a result of the function.

Dmitry
  • 432
  • 2
  • 6
1
y = [1,2]

y ----> [1, 2]

def do_something(arg):
                  ^
                  |
     do_something(y) causes python to do this assignment: arg = y


y ----> [1, 2]
          ^
          |
arg ------+

x = arg 

    y ---->  [1, 2]
              ^  ^
              |  |
    arg ------+  |
                 |      
    x -----------+

Then, using either x or arg to change the list will cause the list that y sees to change. In other words, all the variables refer to the same list.

I even (try to) create a new copy of arg (x) to prevent it. Even if I dont return x, I still get the same result.

Not true:

def do_something(arg):
    x = arg[:]
    x[0] = 9
    return x

y = [1,2]
result = do_something(y)
print y
print result

--output:--
[1, 2]
[9, 2]
7stud
  • 46,922
  • 14
  • 101
  • 127
1

Everything is a reference in Python. If you wish to avoid that behavior you would have to create a new copy of the original with list(). If the list contains more references, you'd need to use deepcopy()

If you print the address of x and y, you will figure out that their addresses are the same, which causes the result.

def do_something(arg):
    x=arg
    x[0] = 9
    print "address of x:", id(x)
    return x

y = [1,2]

do_something(y)
print "address of y:", id(y)
Community
  • 1
  • 1
Hooting
  • 1,681
  • 11
  • 20