-1

How come the following code:

a = [1,2,3]
b = a
b[0] = 3
print(a)

will print list b after it has been altered?[3,2,3]. Also why is this true but that the following code:

a = [1,2,3]
b = a
b = [0,0,0]
print(a,b)

prints [1, 2, 3] [0, 0, 0]?? This seems inconsistent. If the first code is true, then shouldn't the second code print [0,0,0][0,0,0]? Can someone please provide an explanation for this?

k lol
  • 3
  • 1
  • 5
    In the first case you are reassigning a particular element (`b[0]`) so it still refers to the initial `a` list. In the second case, you are completely reassigning/overwriting it with a new list and so the initial reference to `a` is overwritten – Sheldore Nov 03 '18 at 19:59
  • You might find [this](https://stackoverflow.com/questions/6793872/variable-assignment-and-modification-in-python) helpful in understanding the concept behind. – Sheldore Nov 03 '18 at 20:19
  • `b = a` does NOT create a separate copy of `a`. Instead, `a` and `b` now both refer to the **same** value. Think of it like a label or a nametag; you can walk around at a party wearing a nametag that says "Hi I'm Bob" and another nametag that says "Hi I'm Steve". Bob and Steve are both names for the same person -- you. – John Gordon Nov 03 '18 at 22:40

1 Answers1

0

In python there are two types of data... mutable and immutable. Numbers, strings, boolean, tuples, and other simple types are immutable. Dicts, lists, sets, objects, classes, and other complex types are mutable.

When you say:

a = [1,2,3]
b = a

You've created a single mutable list in memory, assigned a to point to it, and then assigned b to point to it. It's the same thing in memory.

Therefore when you mutate it (modify it):

b[0] = 3

It is a modification (mutation) of the index [0] of the value which b points to at that same memory location.

However, when you replace it:

b = [0,0,0]

It is creating a new mutable list in memory and assigning b to point at it.


Check out the id() function. It will tell you the "address" of any variable. You can see which names are pointing to the same memory location with id(varname).


Bonus: Every value in python is passed by reference... meaning that when you assign it to a variable it simply causes that variable to point to that value where it was in memory. Having immutable types allows python to "reuse" the same memory location for common immutable types.

Consider some common values when the interpreter starts up:

>>> import sys
>>> sys.getrefcount('abc')
68
>>> sys.getrefcount(100)
110
>>> sys.getrefcount(2)
6471

However, a value that is definitely not present would return 2. This has to do with the fact that a couple of references to that value were in-use during the call to sys.getrefcount

>>> sys.getrefcount('nope not me.  I am definitely not here already.')
2

Notice that an empty tuple has a lot of references:

>>> sys.getrefcount(tuple())
34571

But an empty list has no extra references:

>>> sys.getrefcount(list())
1

Why is this? Because tuple is immutable so it is fine to share that value across any number of variables. However, lists are mutable so they MUST NOT be shared across arbitrary variables or changes to one would affect the others.

Incidentally, this is also why you must NEVER use mutable types as default argument values to functions. Consider this innocent little function:

>>> def foo(value=[]):
...     value.append(1)
...     print(value)
...
...

When you call it you might expect to get [1] printed...

>>> foo()
[1]

However, when you call it again, you prob. won't expect to get [1,1] out... ???

>>> foo()
[1, 1]

And on and on...

>>> foo()
[1, 1, 1]

>>> foo()
[1, 1, 1, 1]

WHY IS THIS? Because default arguments to functions are evaluated once during function definition, and not at function run time. That way if you use a mutable value as a default argument value, then you will be stuck with that one value, mutating in unexpected ways as the function is called multiple times.

The proper way to do it is this:

>>> def foo(value=None):
...     if value is None:
...         value = []
...     value.append(1)
...     print(value)
...
...
>>>
>>> foo()
[1]
>>> foo()
[1]
>>> foo()
[1]
gahooa
  • 131,293
  • 12
  • 98
  • 101