Recitation 3: Lists

Lists! Lists are everywhere. Pretty much every program you’ll write will involve a list in some fashion, so it’s important to be able to use them competently.

Lists

So what’s a list? It’s not that different from lists outside of computer science. It’s just a collection of things. These can be anything. For example, here is an organized list.

[1, 2, 3, 4, 5]

Pretty organized. But this disorganized lump of stuff is also a list!

[True, 0, 3.62, "Obama"]

The types don’t even match! But Python doesn’t care. Anything can be in a list. What about… other lists? Yup, that works, too!

[False, -8, 2.128, []]

This particular list has an empty list inside of it! An empty list is denoted with [] not ( ).

Imagine you want to access the zeroeth element in a list (remember, computer scientists count from 0, not 1). How would we do this? From the notes, you’d probably say, if the list name was popStars, you’d say popStars[0].

We call this indexing into a list.

What happens if we try this:

a = [1, 2, 3, 4, 5]
print a[5]

As we said above, lists are indexed starting at index 0. Which means that the list a has indices 0 through 4. We get the following error.

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

Again, the last line is the important thing here. An IndexError is Python’s way of telling you that you’re accessing an element that doesn’t exist.

But what about types!? Remember that type function?(I told you I’d ask you about types everyday!) Let’s see what Python says.

>>>type([1, 2, 3])
<type 'list'>

Ah-ha! A new type called list. The type list is not a primary type. (Remember, primary types only include int, str, float and bool.) This new type is a complex type. As you can imagine, functions can also return lists.

With lists come a new operator in. You’ve seen in in for loops, but with lists they can be used differently: to test if something is “in” a list.

badOlympicThings = ["Invade Crimea", "Start World War 3",
    "Not have all the Olympic Rings Work",
    "Kill the engineer who made the olympic rings not work"]

if "Invade Crimea" in badOlympicThings:
    print "Oh, this is probably a bad idea. Guess I shouldn't do that."

print "Lol JK, I'm Putin and I do what I want."

The above code will execute both print statements.

Splice Notation

In Python, there are actually two ways to index into a list! Let’s visualize it with this handy diagram.

 +---+---+---+---+---+
 | H | e | l | p | A |
 +---+---+---+---+---+
 0   1   2   3   4   5
-5  -4  -3  -2  -1

So with negative numbers, you can start indexing from the right side instead of the left side! This way you don’t actually have to know the length of the list. Imagine you wanted to get the right-most element in a list. Without negative indexing, you’d have to do something annoying like this.

def getLastElement(someList):
    return someList[len(someList) - 1

So why do we subtract by one? Remember, (I’ll keep saying it until it’s stuck in everyone’s heads,) computer scientists start counting from zero. This means that if a list has 3 elements (that’s the length of the list), the indices are 0, 1, and 2. That means the index of the last element is the length minus one.

But that’s annoying. Why can’t we just do

return someList[-1]

We can. Yay Python!

But with this new notion of awesome negative indexing, we have what’s called splice notation. Splice notation is a fast way to make sublists given an original list.

My friends know I love spoilers. spoilers. Reading the entire Harry Potter series just seems like so much work! Why when I can just have spoilers that save me all that work. So here’s an example of the giant list of spoilers my friends have compiled for me.

harry_potter_spoilers = ["Sirius Black is innocent and Harry's godfather",
    "Peter Pettigrew is the traitor", "Dumbledore dies",
    "Harry is the last Horcrux"]

But I’m so lazy that I don’t even want to read through this list of spoilers in order. I want to read it backwards! After all, the most important spoilers must be at the end, right? So let’s do that.

for spoiler in harry_potter_spoilers[::-1]:
    print spoiler

What!? No list reversal? Nope. Splice notation, with just a few extra characters, handles all of that for us! So let’s see how this works.

list[start:end] # items start through end-1
list[start:]    # items start through the rest of the array
list[:end]      # items from the beginning through end-1
list[:]         # a copy of the whole array
list[start:end:step] # start through not past end, by step

The point that most beginners find confusing is that end represents the index of the first element not included. This is just like range. range(4) includes everything but up to but not including 4.

Here are some further examples of splice notation.

list[-1]    # last item in the array
list[-2:]   # last two items in the array
list[:-2]   # everything except the last two items

So how does the example I gave above work? Remember that if the start and end values are left empty, splice notation defaults to the start and end of the list. That’s why list[:] copies the entire list. However, we’ve added a third parameter, the step of -1. Thus, it copies the entire list, but steps through it backwards. In essence, this reverses the list, which is what we wanted!

Remember, this creates copies of lists. So if you want to change the actual elements within a list, you’ll have to use the for loop that uses range: for <var> in range(len(<list name>)):.

Reference

Here are some common list functions that you may find useful. You’re not expected to memorize them, but be familiar enough that if presented on an exam you’ll know what they do.

Common List Functions

max finds the largest element in a list.

>>> a = [1, 2, 3, 4]
>>> max(a)
4

min finds the smallest.

>>> a = [1, 2, 3, 4]
>>> min(a)
1

len, which you’ve already seen, gives the length of a list.

>>> a = [1, 2, 3, 4]
>>> len(a)
4

Common List Methods

This begs the question of what’s the difference between a method and a function. We’ll get to that in two weeks. For now, here is the difference.

A function that uses a:

function(a)

A method of a:

a.function()

So all of these that I’m about to show you are methods, which means the format is listName.methodName().

append adds something to the end of a list.

>>> a = [1, 2, 3, 4]
>>> a.append(5)
>>> print a
[1, 2, 3, 4, 5]

count takes a parameter and counts how many times that parameter appears in the list.

>>> a = [1, 2, 1, 3]
>>> a.count(2)
2

reverse makes the list backwards.

>>> a = [1, 2, 3, 4]
>>> a.reverse()
>>> print a
[4, 3, 2, 1]