Indexing¶

🖌 Swap¶

NOTES

  • Object lesson:
    • have two students come up with their backpacks
    • rule: they can only hold one backback at a time
    • goal: swap backpacks
    • solution: you need a third person to hold a backpack in order to complete the trade
In [ ]:
thing1 = 'abacus'
thing2 = 'bobbin'
print(f'Thing 1 is {thing1}. Thing 2 is {thing2}.')
In [ ]:
tmp = thing1
thing1 = thing2
thing2 = tmp

print(f'Thing 1 is {thing1}. Thing 2 is {thing2}.')

NOTES

  • draw this out
    • 'abacus' and 'bobbin' on the heap, variables thing1 and thing2
    • new variable tmp pointing to the same value as thing1 (abacus)
    • Now that someone else is pointing to abacus, thing1 can point to someone else
      • point it to the same value as thing2 (bobbin)
    • now that someone else is pointing to bobbin, thing2 can point to someone else
      • point it to the same value as tmp (abacus)
    • now the variables thing1 and thing2 have swapped their values

🎨 Indexing []¶

In [ ]:
fruits = ['plums', 'peaches', 'pears', 'persimmons']
fruits[0]
In [ ]:
fruits[1]
In [ ]:
fruits[3]

NOTES

  • Indexing starts at 0
    • The first item is at [0]
    • The last item is $length - 1$
In [ ]:
fruits[7]

NOTES

  • Indexing beyond the final element gives you an IndexError
In [ ]:
fruits = ['plums', 'peaches', 'pears', 'persimmons']
fruits[-1]
In [ ]:
fruits[-3]
In [ ]:
fruits[-4]

NOTES

  • negative indexes work from the back of the list
  • $-1$ is the last element, etc.
In [ ]:
fruits[len(fruits)-1], fruits[-1]

NOTES

  • mnumonic: add the negative index to the length of the list to get the corresponding positive index
  • I usually only use negative indexes to retrieve the last value, or maybe second to last.

🎨 range¶

In [ ]:
for i in range(5):
    print(i)

NOTES

  • What numbers were printed? (i.e. started with 0) What numbers were NOT printed? (i.e. 5)
In [ ]:
type(range(5))
In [ ]:
list(range(5))

NOTES

  • range returns...a range object.
    • a range object is iterable, like strings, lists, and files
  • So, it is not a list, but because it is iterable, you can turn it into a list
In [ ]:
fruits = ['apples', 'apricots', 'avacados']
for index in range(len(fruits)):
    print(fruits[index])
    
# Same as
for fruit in fruits:
    print(fruit)
In [ ]:
 

NOTES

  • discuss this code before you run it
    • what numbers will range(len(fruits)) produce? Write them out.
    • why did we name those numbers index?
    • for each value of index, what does fruits[index] print?
In [ ]:
list(range(4, 12))

NOTES

  • discuss this code before you run it: what do you think it should produce?
  • range(a, b) (two arguments) starts at a inclusive and goes to b exclusive.
In [ ]:
list(range(4, 12, 2))

NOTES

  • discuss this code before you run it: what do you think it should produce?
  • range(a, b, c) (three arguments) starts at a inclusive and goes to b exclusive with a step size of c.

👨🏼‍🎨 Ranges¶

What python expression using range would you use to create the following sequences?

  1. 1, 2, 3, 4, 5
  2. 0, 2, 4, 6, 8
  3. -3, 0, 3
  4. 5, 4, 3, 2

NOTES

  • Work alone for 30 seconds and write down each expression
  • Then compare with a neighbor - do you have the same answers?
  • range(a, b, c) (three arguments) starts at a inclusive and goes to b exclusive with a step size of c.
In [ ]:
list(range(1, 6))
In [ ]:
list(range(0, 10, 2))
In [ ]:
list(range(-3, 6, 3))
In [ ]:
list(range(5, 1, -1))

🖌 🗡 List Mutability¶

In [ ]:
bears = ['brown','black','polar','koala']
bears
In [ ]:
bears[3]
In [ ]:
bears[3] = 'panda'
In [ ]:
bears[3]
In [ ]:
bears

NOTES

  • Lists are mutable
    • not only can we make them longer with .append(), we can change the value a given index points to
  • Draw this out
    • original values on the heap
    • a list on the heap with 4 slots pointing to the original values
    • a new value ('panda') on the heap and the last slot now pointing to it
    • 'koala' is still on the heap, just nothing is pointing to it at the moment
  • we can change an item in a list just like we would change a variable.
    • the value itself doesn't change, but our reference changes to point to a different value

Lists and Functions¶

In [ ]:
def swap_first_and_last(items):
    """Swap the first and last items in the input list. Modify the list in place."""
    first = items[0]
    items[0] = items[-1]
    items[-1] = first
    
numbers = [1, 2, 3, 4, 5]
print(numbers)
swap_first_and_last(numbers)
print(numbers)
swap_first_and_last(numbers)
print(numbers)

NOTES

  • DRAW THIS OUT
    • 1, 2, 3, 4, 5 on the heap, list referencing values on heap, variable numbers pointing to the list
    • Seperate variable namespace for swap_first_and_last with variable items
    • Function call: items now points to same value as numbers. numbers continues to point to the list.
    • Reassignment: using items, the function changes the references in the list.
    • Function ends: numbers still points to the same list.
    • When list is printed, we see that the 5 and 1 have traded places.
Because lists are mutable, anyone with a reference to the list has the ability to change it's contents.

Just because a function can change a list doesn't mean it should.

Prefer immutability whenever possible: only change inputs when there is a clear advantage over making a copy

Clearly define and document the intent of your functions.

  • does a function change the input or only read the input?

🖌 Swap¶

(The Python Way)

In [ ]:
a = 7
b = 2
print(a, b)

a, b = b, a
print(a, b)

NOTES

  • This uses tuple unpacking
    • Essentially, there are two temporary slots created to hold the values of a and b, and then they are both reassigned at the same time

🖌 Indexing with Modulus¶

In [ ]:
numbers = list(range(1, 4))
print(numbers)
In [ ]:
numbers = list(range(1, 4))
print(numbers)

more_numbers = []

for i in range(10):
    i_mod = i % len(numbers)
    next_number = numbers[i_mod]
    more_numbers.append(next_number)

print(more_numbers)
i i_mod numbers[i_mod]
0 0 1
1 1 2
2 2 3
3 0 1
4 1 2
5 2 3
6 0 1
7 1 2
8 2 3
9 0 1

NOTES

  • Discuss the code before running it. What will be the outcome?

Key Ideas¶

  • Swap
  • Indexing []
  • range
    • one argument $a$: goes from 0 to $a$ (exclusive)
    • two arguments $a$ and $b$: goes from $a$ (inclusive) to $b$ (exclusive)
    • three arguments $a$, $b$, and $c$: goes from $a$ (inclusive) to $b$ (exclusive) with a step size of $c$
  • Lists are mutable
    • they can grow
    • you can reassign new values to individual positions
    • don't write functions that will surprise you with unexpected changes to an input list
  • Modulus (%) and indexing