Lists

Note

Be sure to activate your virtual environment in your command/terminal window before starting the Python REPL.

List Basics

Lists in Python are ordered collections of things. Lists and list elements are designated by square brackets, [].

>>> things = [5, True, "hello"]
>>> things
[5, True, 'hello']

Just like every other object, we can use type to confirm that we’re working with a list:

>>> type(things)
<class 'list'>

Like strings, lists have a length.

>>> len(things)
3

Also like strings, lists can be checked for containment.

>>> 5 in things
True
>>> False in things
False
>>> "hello" in things
True

Lists are ordered. We can access individual items in lists based on their position.

Lists are zero-indexed so the first item has an index of zero.

>>> things[0]
5
>>> things[2]
'hello'

If we try to access an element outside of our list, we’ll get an exception.

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

Elements of the list can be of different types:

>>> type(things[0])
<class 'int'>
>>> type(things[1])
<class 'bool'>

We can change items in our lists using a syntax similar to setting variables:

>>> things[1] = False
>>> things
[5, False, 'hello']

Last List Element

Let’s say we have access to a list and we don’t know much about it.

How can we get the last element from our list?

One way:

>>> things[len(things) - 1]
'something else'

Python gives us a shortcut though. We can use negative indexes to start counting from the end of our list.

To get the last element we can use index -1:

>>> things[-1]
'something else'

Negative indexing starts from the end of the string and counts in backwards:

>>> things[-2]
'hello'

List Methods

We can add things to the end of our list using the append method:

>>> things.append("something else")
>>> len(things)
4
>>> things[3]
'something else'
>>> things
[5, False, 'hello', 'something else']

Strings have a method called join which allows us to join a collection of string items together, delimiting them with the initial string characters. Let’s use it with a list:

>>> words = ["these", "are", "some", "words"]
>>> " ".join(words)
'these are some words'
>>> "#".join(words)
'these#are#some#words'
>>> "...".join(words)
'these...are...some...words'

Since strings are themselves a collection of characters, we can also use the join method on a string:

>>> the_word = "bird"
>>> " ".join(the_word)
'b i r d'

Lists have other methods too. If we want to see other methods that lists have we can use the help function.

Remember that we can use arrows to move up and down in help and q to quit help. Vim keyboard shortcuts also work.

We can use help on the list function:

>>> help(list)

Or we can use help directly on our list instance variable. We’ll get the same thing both ways.

>>> help(things)

We could also find out more about lists by searching the Python documentation.

When searching for Python documentation, use a search engine like Google or DuckDuckGo. Python’s documentation search is not very good.

List Exercises

Combined Lists

Make a function that takes two lists and returns a new list containing all elements from both lists. It should work like this:

>>> first = [1, 2, 3]
>>> second = [4, 5, 6]
>>> combine_lists(first, second)
[1, 2, 3, 4, 5, 6]

Remove first item

Remove the first item from a list.

Remove last item

Remove the last item from a list.

Remove item by value

Make the following list:

>>> names = ["Alice", "Bob", "Christy", "Jules"]

Remove “Bob” from our list without referencing its index number.

Join Words

Create a list of words:

>>> words = ["these", "are", "some", "words"]

In one line of code, print all words in the list to the terminal, separating them each by a line break.

Slicing

What if we want to get a consecutive number of elements from a list, but not the whole list?

We can use slices to get elements starting from one index, up to but not including another index.

>>> fruits = ["apples", "oranges", "bananas", "strawberries"]
>>> fruits[0:2]
['apples', 'oranges']
>>> fruits[1:3]
['oranges', 'bananas']

If we want to start our slice from the beginning of the list, we can leave out the first number:

>>> fruits[:2]
['apples', 'oranges']

Similarly if we want to end our slice at the end of the list, we can leave out the last number:

>>> fruits[2:]
['bananas', 'strawberries']
>>> fruits[2:len(fruits)]
['bananas', 'strawberries']

Assigning a list to a new variable does not actually make a copy, it just copies the reference:

>>> my_fruits = fruits
>>> my_fruits[3] = "kiwi"
>>> my_fruits
['apples', 'oranges', 'bananas', 'kiwi']
>>> fruits
['apples', 'oranges', 'bananas', 'kiwi']

But using the slice notation returns a new list containing just the elements specified. So by extension, one way to completely copy a list would be to use the slice notation and leave off both sides of the slice:

>>> favorite_fruits = fruits[:]
>>> favorite_fruits.pop()
'kiwi'
>>> favorite_fruits
['apples', 'oranges', 'bananas']
>>> fruits
['apples', 'oranges', 'bananas', 'kiwi']

Slices also allow for a third number that represents the step. The default step is 1. If we increase the step, the gap between our returned indices will increase:

>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> numbers[::1]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> numbers[::2]
[1, 3, 5, 7, 9]
>>> numbers[::3]
[1, 4, 7]

If we use a negative number our slice will start counting from the end of the list:

>>> numbers[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> numbers[::-2]
[9, 7, 5, 3, 1]

Slice Exercises

Last N Elements

Write a function that returns the last items of a list

  • Make a list with 5 of your favorite fruits
  • Write a slice that gets the last 3 fruits

Last N words

  • Create a string containing the declaration of independence
  • Get a list of the last 6 words from the declaration of independence
  • Get a string of the last 31 words from the declaration of independence

Last Words

Create a function that takes a string as an argument and will return a new string containing the last two words of the given string, separated by a space.

It should work like this:

>>> last_two(declaration_of_independence)
'sacred Honor.'
>>> last_two("hello world")
'hello world'
>>> last_two("hi")
'hi'

Half

Make a function that splits a list in half and returns both halves. It should work like this:

>>> split_in_half([1, 2, 3, 4])
([1, 2], [3, 4])
>>> split_in_half([1, 2, 3, 4, 5])
([1, 2], [3, 4, 5])
>>> split_in_half([1, 2])
([1], [2])
>>> split_in_half([])
([], [])
>>> split_in_half([1])
([], [1])

Try your function on other iterables (strings or tuples). Does it still work?

>>> split_in_half("Hello world!")
('Hello ', 'world!')
>>> split_in_half((1, 2))
((1,), (2,))

Lists & Truthiness

Lists evaluate to falsey if they are empty and truthy otherwise:

>>> numbers = []
>>> if numbers:
...     print("Non-empty")
...
>>> numbers.append(5)
>>> numbers
[5]
>>> if numbers:
...     print("Non-empty")
...
Non-empty

A list containing only the value “None” is still truthy because it contains an element:

>>> numbers = []
>>> numbers.append(None)
>>> if numbers:
...     print("Non-empty")
...
Non-empty

String Indexes

Strings can be indexed and sliced just like lists:

>>> hello = "Hello world"
>>> hello[1]
'e'
>>> hello[3:8]
'lo wo'
>>> hello[-1]
'd'

However, strings are immutable, so you cannot modify them:

>>> hello[4] = "a"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

If you ever see code that appears to modify a string, look closer. The code is returning a new string, not modifying the original.

>>> hello = hello.lower()
>>> hello
'hello world'
>>> hello += "!"
>>> hello
'hello world!'