0

I was making a code, and variables started to behave strangely and get assigned to things which I thought they shouldn't. So, I decided to reduce the situation to minimal complexity in order to solve my doubts, and this is what happened:

The following code:

a = [2]

def changeA(c):
    d = c
    d[0] = 10
    return True

changeA(a)

print(a)

prints '[10]'. This doesn't make sense to me, since I never assigned the list "a" to be anything after the first assignment. Inside the function changeA, the local variable d is assigned to be the input of the function, and it seems to me that this assignment is happening both ways, and even changing the "outside". If so, why? If not, why is this happening?

I've also noticed that the code

a = [2]

def changeA(c):
    d = list(c)
    d[0] = 10
    return True

changeA(a)

print(a)

behaves normally (i.e., as I would expect).

EDIT: This question is being considered a duplicate of this one. I don't think this is true, since it is also relevant here that the locality character of procedures inside a function is being violated.

  • 3
    `d = c` doesn't make a new list. See https://nedbatchelder.com/text/names.html – user2357112 May 31 '18 at 21:41
  • 1
    `d = c` just says "the object referenced by the name `c` is now referenced by the name `d`". So `d` and `c` refer to *the same object*. – juanpa.arrivillaga May 31 '18 at 21:47
  • Possible duplicate of https://stackoverflow.com/q/2612802/6622817 – Taku May 31 '18 at 21:49
  • @juanpa.arrivillaga Okay, I'll read into that. Thanks in advance. But I still don't get why it is interferring with what is *outside* the function. – Aloizio Macedo May 31 '18 at 21:50
  • @AloizioMacedo: Parameter passing doesn't make a new list either. – user2357112 May 31 '18 at 21:51
  • @AloizioMacedo because **they are all referring to the same object**. Parameter passing in Python works essentially like assignment to a local variable. Read the link posted above to the Ned Batchelder article on Facts and Myths about Python names and values. If you read it all and understand it, the behavior you are seeing will be very obvious and clear. – juanpa.arrivillaga May 31 '18 at 21:52
  • @juanpa.arrivillaga I'll read it, and then if I have any doubts I'll be back to you later. Thanks! – Aloizio Macedo May 31 '18 at 21:54
  • @abccd definitely not a duplicate of *that* target, but I'm sure there is an appropriate duplicate *somewhere* – juanpa.arrivillaga May 31 '18 at 21:54
  • 1
    Possible duplicate of [How do I pass a variable by reference?](https://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference) – Thierry Lathuille May 31 '18 at 22:08
  • Possible duplicate of [Python: Modify Global list inside a function](https://stackoverflow.com/questions/31435603/python-modify-global-list-inside-a-function) – Barmar May 31 '18 at 22:08

6 Answers6

2

Python variables are references to objects, and some objects are mutable. Numbers are not, neither are strings nor tuples, but lists, sets and dicts are.

Let us look at the following Python code

a = [2]       # ok a is a reference to a mutable list
b = a         # b is a reference to the exact same list
b[0] = 12     # changes the value of first element of the unique list
print(a)      # will display [12]
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
1

In the first example, you simply pass the reference of c(which is a) to d.

So whatever you do to d will happen on a.

In the second example, you copy the value of c(which is a) and give it to a new variable d.

So the d now has the same value as c(which is a) but different reference.

Note: you can see the reference or id of a variable using the id() function.

a = [2]
print id(a)

def changeA(c):
    d = c
    pirnt id(d)
    d[0] = 10
    return True

changeA(a)

print(a)


a = [2]
print id(a)
def changeA(c):
    d = list(c)
    print id(d)
    d[0] = 10
    return True

changeA(a)

print(a)
Zihao Zhao
  • 439
  • 5
  • 9
1

Its because when you do:

 d = list(c)

that creates a new object. But when you do

d = c

You are making a reference to that object.

if you did

d.append(5)

to the first example you would get

[10,5]

Same operation to the second one and the list isn't modified.

Deeper explanation in the following link: http://henry.precheur.org/python/copy_list

Kalimantan
  • 702
  • 1
  • 9
  • 28
0

In Python, names refer to values, so you have 2 names pointing to the same value.

Computer's Guy
  • 5,122
  • 8
  • 54
  • 74
0

In version 1 you create a list a, pass it in to the function under the pseudonym c, create another pseudonym d for the very same list, and then change the first value in that list. a, c, and d all refer to the same list.

In version 2, you're using list(c) which, to Python, means "take the contents of this iterable thing named c and make a new, different, list from it named d". Now, there are two copies of your list floating around. One is referred to as a or c, and the other is d. Therefore, when you update d[0] you're operating a second copy of the list. a remains unchanged.

Larry Lustig
  • 49,320
  • 14
  • 110
  • 160
0

'd=c' means reference copy as stated before. What it means, is that now d will reference the same object as c. As you are doing a direct manipulation on the referenced object, the value of the object a was referencing to is changed as well.

When you do 'd = list(c)' what it means that a new list object is created, with the baked of c. However, d is not referencing the same object as a anymore. Hence, the changes within the function doesn't impact a.

baruchl
  • 219
  • 2
  • 13