An object is mutable if you can modify or change its data.
What's the output of the following code? →
a = [1, 2, 3]
b = "123"
a[1] = 1
print(a)
b[1] = "1"
print(b)
[1, 1, 3]
Traceback (most recent call last):
File "indexing_and_assignment.py", line 5, in <module>
b[1] = "1"
TypeError: 'str' object does not support item assignment
You can change values in a list!
really_famous_cats = ["nermal", "felix", "sylvester"]
really_famous_cats[0] = "garfield"
# assignment works just fine!
The behavior of the methods in lists and strings are consistent with the mutability of each type:
What does this code output? →
a = "one two three"
b = [2, 1, 3]
result_upper = a.upper()
print(a)
print(result_upper)
result_sort = b.sort()
print(b)
print(result_sort)
one two three
ONE TWO THREE
[1, 2, 3]
None
What's a variable? →
Imagine variables as names that point to objects:
a = [1, 2, 3]
a ------> [1, 2, 3]
Assignment is just pointing a reference. When a new value is assigned to a name, it's the reference that's being changed.
In the following reassignment example, notice that there are no more names that reference the initial list, [1, 2, 3].
a = [1, 2, 3]
a = [4, 5, 6]
[1, 2, 3]
a ------> [4, 5, 6]
When one variable is assigned to another variable, both variables end up referring to the same object:
a = [1, 2, 3]
b = a
a ---+
|--> [1, 2, 3]
b ---+
The actual list object, [1, 2, 3], now has two names that refer to it. Referencing the same object with more than one name is called aliasing.
In the code below, a and b refer to the same list. What will the values of a and b be if we append 4 to b? →
a = [1, 2, 3]
b = a
b.append(4)
print(a)
print(b)
[1, 2, 3, 4]
[1, 2, 3, 4]
See in python tutor
Aliasing causes side effects in mutable objects! However, if an object is immutable, like a string, these side effects don't occur (since the object can't be changed anyway!). What gets printed out here? →
a = "hello"
b = a
b.upper()
print(a)
print(b)
hello
hello
# b never changed, so neither did a
If you'd like to make a new list rather than refer to the same list (that is have each variable point to a different object - though two equal objects)…
a ------> [1, 2, 3]
b ------> [1, 2, 3]
…you can use list slicing, which always gives back a new list. Creating a new list that is equivalent to, but a different object from the original, is called cloning.
You can slice out the entire list to clone a list from the start index (0) to end index (len(list_of_elements) - 1):
a = [1, 2, 3]
# cloned!
b = a[0:3]
Alternatively, there's shortcut to slicing out the whole string (without having to deal with precise start and end indexes)…
Just leave out the start and end (m, n) index from the slice.
b = a[:]
When parameters are passed to functions the value is a reference! What will this code print out? →
a = [1, 2, 3]
def add_to_list(stuff):
stuff.append(1)
add_to_list(a)
print(a)
[1, 2, 3, 1]
See in Python tutor
Changes made to a mutable object that's an argument to a function can be seen both within and outside of the function (all refer to the same object!). →
The following function finds the largest integer in a list of integers, but it inadvertently sorts the original.
# hm... maybe not the best way to find the greatest, but ...
def find_greatest(numbers):
numbers.sort()
return numbers[-1]
my_numbers = [5, 6, 1, 4]
greatest = find_greatest(my_numbers)
#hey wait a second, this isn't the original list!
print('original list:', my_numbers)
print(greatest)