Mutability

CSCI 1913 – Introduction to Algorithms, Data Structures, and Program Development
Adriana Picoral

Lists are mutable

  • You can change lists after they have been created
    • This change does not cause the creation of a new object

built-in id() function

  • Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.
  • CPython implementation detail (Probably your python): this is the address of the object in memory.

Lists are mutable

  • You can change lists after they have been created
    • This change does not cause the creation of a new object
numbers = [20, 10, 2, 3, 5, 10]
id(numbers)
4618079616
numbers[1] = 99
id(numbers)
4618079616
numbers += [99]
id(numbers)
4618079616

Tuples are NOT mutable

Contrast lists that with tuples:

numbers = (20, 10, 2, 3, 5, 10)
id(numbers)
5858243680
numbers += (99,)
id(numbers)
5858242720

Lists are mutable

The two functions below are similar, but have one major difference in what they do: What is it?

def double1(num_list):
  out_list = []
  for i in num_list:
    out_list += [(i * 2)]
  return out_list

Compared with:

def double2(num_list):
  for i in range(len(num_list)):
      num_list[i] *= 2
  return num_list

What is the major difference?

Lists are mutable

The function double1 builds a new list to return without affecting num_list; double2 changes the list passed in as argument.

Lists are mutable

Different names can reference same object:

fruit = ["apple", "banana", "pineapple"]
same_fruit = fruit

# change list
same_fruit[0] = "pear"

# print both lists
print(fruit)
print(same_fruit)
['pear', 'banana', 'pineapple']
['pear', 'banana', 'pineapple']

Lists are mutable

Two for loops, one changes the list, the other does not:

numbers = [1, 2, 1, 4]
for n in numbers:
  n = n * 2
print(numbers)
[1, 2, 1, 4]

Compared with:

numbers = [1, 2, 1, 4]
for i in range(len(numbers)):
  numbers[i] = numbers[i] * 2
print(numbers)
[2, 4, 2, 8]

What are the differences? Why does one change the list and the other does not?

Exercise

Write a python function called lowercase_list that gets as argument a list of strings and modifies the list by lowercasing each item in the list. You can use the string method .lower().

Test case:

fruit = ["APPLE", "BANANA", "PEAR"]
lowercase_list(fruit)
assert fruit == ["apple", "banana", "pear"]

Submit your lowercase_list.py file to gradescope

Solution

We need the index to change items in a list.

def lowercase_list(my_list):
    for i in range(len(my_list)):
        my_list[i] = my_list[i].lower()

list methods

These are some of the methods you talked about:

  • .append(item) adds an item to the end of the list
  • .insert(index, item) adds an item to index position (rest of list shifts right)
  • .remove(item) removes the first matching item
  • .pop(index) removes item at index (returns the removed value)

More list methods

There are more:

  • .index(item) returns the index of the first matching item, throws error if no match is found
  • .copy() returns a copy of the list (different id), can also copy with slicing [:]
  • .sort() changes the list, sorts list in place, returns None (contrast with sorted() function)
  • .reverse() changes the list, reverses list in place, returns None (contrast with [::-1])

+ vs +=

add (the + operator) vs iadd (the += operator)

  • both perform addition
  • they differ in how they handle object modification.
fruit = ["apple", "pear"]
print(id(fruit))
vegetable = ["kale", "spinach"]
fruit += vegetable  # Modifies list in place (iadd)
print(id(fruit))
4584222784
4584222784

+ vs +=

add (the + operator) vs iadd (the += operator)

  • both perform addition
  • they differ in how they handle object modification.
fruit = ["apple", "pear"]
print(id(fruit))
vegetable = ["kale", "spinach"]
fruit = fruit + vegetable  # returns new list (add)
print(id(fruit))
4584340736
4617964928

Immutable objects

For immutable objects, += returns a new object

number = 10
print(id(number))
another_number = 30
number += another_number
print(id(number))
4400031200
4400032160

Exercise

Write a python function called multiply_items that takes two arguments: a list and an integer n.

The function should return a new list, with each item in the original list multiplied by n.

Test case:

things = ["apple", 3, 4.0]
assert multiply_items(things, 3) == ["apple", "apple", "apple", 3, 3, 3, 4.0, 4.0, 4.0]

Submit your list_multiplication.py file to gradescope.

Solution

def multiply_items(my_list, n):
    result = []
    for item in my_list:
        result += [item] * n
    return result
  
if __name__ == "__main__":
    things = ["apple", 3, 4.0]
    print(multiply_items(things, 3))
['apple', 'apple', 'apple', 3, 3, 3, 4.0, 4.0, 4.0]