mutability (slides)

CSc 110 - Mutability

Lists vs strings

  • Lists are mutable

  • Strings are not

  • In addition to retrieving an item from a list, we can remove or change it (which is not something you can do with strings)

songs = ["Lavender Haze", "Calm Down", "As It Was", "About Damn Time"]
songs
['Lavender Haze', 'Calm Down', 'As It Was', 'About Damn Time']
songs[0] = "Flowers"
songs
['Flowers', 'Calm Down', 'As It Was', 'About Damn Time']

Lists are mutable

(strings are not)

Because lists are mutable:

  • once a list is changed inside a function, that change persists when the function has finished running

Write a function

  1. Its name is make_even
  2. It takes one argument, a list of integers
  3. It iterates over the list, changing odd numbers to even number (even up)

Name your file make_even.py and submit to Gradescope.

integers = [1, 2, 3, 4]
make_even(integers)
assert integers == [2, 2, 4, 4] # change persists

Write a function – solution 1

def make_even(integers):
  index = 0
  while index < len(integers):
    if integers[index] % 2 == 1:
      integers[index] += 1
    index += 1
  return integers
      
def main():
  integers = [1, 2, 3, 4]
  make_even(integers)
  assert integers == [2, 2, 4, 4]
  
main()

Let’s visualize this on Python Tutor

Write a function – solution 2

def make_even(integers):
  index = 0
  while index < len(integers):
    integers[index] += integers[index] % 2
    index += 1
  return integers
      
def main():
  test_integers = [1, 2, 3, 4]
  make_even(test_integers)
  assert test_integers == [2, 2, 4, 4]
  
main()

If we remove the return statement on line 6, will the function still work properly?

Object References

  • A variable doesn’t store values, it stores a reference to an object that lives in your computer memory (RAM)

  • If you assign a value to an existing object, the variable references that object

last_name = "Brown"
name = last_name # both reference the same object "Brown"
  • If you assign it to a new object, the object is created, placed in memory, and then the variable references it
title = "Dr."
title = "Ms." # "Dr." object will be removed from memory

Examples

Strings are not mutable

title = "Dr."
last_name = "Brown"
print(title + " " + last_name)

title = "Ms."
print(title + " " + last_name)

name = last_name # both point to the same object
last_name = "Silva" # a new object created, last_name point to the new 
print(title + " " + name) # name still point to the original string
Dr. Brown
Ms. Brown
Ms. Brown

Visualize these examples in Python Tutor

Examples

Lists are mutable

names = ["Dr.", "Brown"]
print(names[0] + " " + names[1])

names[0] = "Ms."
print(names[0] + " " + names[1])

names_copy = names # both point to the same object
names[1] = "Silva" # the object been mutated, no new object created
print(names_copy[0] + " " + names_copy[1]) # both point to the mutated object
Dr. Brown
Ms. Brown
Ms. Silva

Visualize these examples in Python Tutor

Summary

When working with lists, once they are changed in a function, the changes happen to the object in memory

Changes to lists persist once the function has finished running

.pop() list method

We will be using a few built-in list methods

Here’s how .pop() works:

songs = ["Lavender Haze", "Calm Down", "As It Was", "Her"]
songs
['Lavender Haze', 'Calm Down', 'As It Was', 'Her']
songs.pop(0) # 'Her' index moves from 3 to 2
songs
['Calm Down', 'As It Was', 'Her']
songs.pop(0) # 'Her' index moves from 2 to 1
songs
['As It Was', 'Her']

.pop() items using while loop

songs = ["Lavender Haze", "Calm Down", "As It Was", "Her"]
index = 0
while index < len(songs):
  songs.pop(index)
  
print(songs)
[]

Why we don’t update index in the while loop?

Write two functions

  • The first function returns the original argument list by removing vowels.

  • The second function returns a string without vowels.

Note: list is mutable, but string is not.

assert remove_vowels_list(["b", "a", "n", "a", "n", "a"]) == ["b", "n", "n"]
assert remove_vowels_string("banana") == "bnn"

Write two functions – solution

def remove_vowels_list(characters):
  index = 0
  while index < len(characters):
    if characters[index] in "aeiou":
      characters.pop(index)
    else:
      index += 1 # go to next index only if no item has been removed
  return characters
    
def remove_vowels_string(string):
  new_string = ""
  index = 0
  while index < len(string):
    if string[index] not in "aeiou":
      new_string += string[index]
    index += 1
  return new_string

def main():
  test_characters = ["b", "a", "n", "a", "n", "a"]
  test_string = "banana"
  
  assert remove_vowels_list(test_characters) == test_characters
  assert test_characters == ["b", "n", "n"]
  assert remove_vowels_string("banana") == "bnn"
  
  print(test_characters)
  print(test_string)
      
main()
['b', 'n', 'n']
banana

List methods

We will be using the following list methods in this class:

  • .append() adds an element at the end of the list: list.append(value)
  • .insert() adds an element at the provided index: list.insert(index, value)
  • .pop() removes a specific element at the provided index: list.pop(index)
  • .remove() removes the first element with the provided value: list.remove(value)

.append() list method

songs = ["Lavender Haze", "Calm Down", "As It Was", "Her"]
songs
['Lavender Haze', 'Calm Down', 'As It Was', 'Her']
songs.append("Attention")
songs
['Lavender Haze', 'Calm Down', 'As It Was', 'Her', 'Attention']

Write a function

It returns a list of integers that represent the indices of the vowels in the original list.

Test cases:

assert indices_of_vowels("hello") == [1, 4]
assert indices_of_vowels("") == []
assert indices_of_vowels("aeiou") == [0, 1, 2, 3, 4]

Write a function – solution

def indices_of_vowels(string):
  result = [] # initialize empty list to hold indices
  index = 0 # initialize index
  while index < len(string):
    if string[index] in "aeiou": # check if character is vowel
      result.append(index) # append index to result
    index += 1 # increment index
  return result

def main():
  assert indices_of_vowels("hello") == [1, 4]
  assert indices_of_vowels("") == []
  assert indices_of_vowels("aeiou") == [0, 1, 2, 3, 4]
  print("Passed all tests.")

main()
Passed all tests.

Write a function

It returns a new list with the items of the original list inverted.

Test case:

strings = ["banana", "apple", "grape"]
assert reverse_list(strings) == ["grape", "apple", "banana"]
assert strings == ["banana", "apple", "grape"] # original list unchanged

Write a function – solution

def reverse_list(items):
  index = len(items) - 1 # initialize index
  inverted_list = []
  while index >= 0:
    inverted_list.append(items[index])
    index -= 1
  return inverted_list
    
def main():
  strings = ["banana", "apple", "grape"]
  assert reverse_list(strings) == ["grape", "apple", "banana"]
  assert strings == ["banana", "apple", "grape"]
  print("Passed test")
  
main()
Passed test

Quiz 06

current time

You have 10 minutes to complete the quiz.

No need to write main() function.