03 Lists

Posted on Jun 15, 2026

In this module we will learn about one way we can store multiple values in a single variable. We’ll focus on Python’s built-in list data type and discuss how to access individual values and subsets. We’ll also look at how we can modify a list in various way which will allow us to actually do stuff with them later on.

Lists

A list in Python is probably the most generaly useful way to collect a set of values into a single variable. Let’s make a new script where we can play around with lists called lists.py

# lists.py
my_list = [1, 2, "deepmay", 7.0]
print(f"This is my list {my_list}")
uv run lists.py
This is my list [1, 2, 'deepmay', 7.0]

We can get the length of our list (and most other collections we’ll see) using a little function called len() for length. len takes a single argument—a list or some other kind of value that can be meaningfully said to have a length—and returns an integer representing the length of the list.

At a glance, our list has a length of 4. Let’s make sure the Python function works as expected.

# lists.py
my_list = [1, 2, "deepmay", 7.0]
length_of_list = len(my_list)

print(f"This is my list {my_list}")
print(f"The length of my list is {length_of_list})
uv run lists.py
This is my list [1, 2, 'deepmay', 7.0]
The length of my list is 4

Hell yeah :sunglasses emoji:

Indexing

Lists are ordered meaning that the order in which we put the elements (what we call the values once they are in a collection) into the list are the order they will be kept in. It also means that we can access elements in our list using their position or index. We use [] to access particular values in a collection. With ordered collections like lists, we will use integers inside the square brackets corresponding to the position in the list. Like most other programming languages, indexing in python starts at 0.

Let’s look at the elements in the lists along with their positions to see how we access particular values.

indexvalue
01
12
2“deepmay”
37.0

Referring to the table above, let’s pull out 1 and "deepmay" from our list.

# lists.py
my_list = [1, 2, "deepmay", 7.0]

print(f"This is the first element: {my_list[0]}")
print(f"This is the third element: {my_list[2]}")

# we can also change an element with indexing
my_list[1] = "3"
print(f"This is the changed list: {my_list})
uv run lists.py
This is the first element: 1
This is the third element: deepmay
This is the changed list: [1, '3', 'deepmay', 7.0]

It can be hard to think about counting starting from 0 but after a while it will become natural and may even start to make some kind of sense.

We can also use negative numbers to index starting from the end of the list. For example, we can always use -1 to get the final element of a list. Let’s look at our little index-value table again and add in reverse indexes to get a sense of how it works.

indexvaluereverse index
010
12-3
2“deepmay”-2
37.0-1

We don’t usually need to use reverse indexing like this except to get the final element with -1 but its worth seeing just in case.

Slicing

While we may sometimes want to accewss individual list elements directly with indexing, there’s more we can do. Python allows us to pull out slices—contiguous sequences of elements—from our list by providing a start and end index value in our square brackets along with a colon separating them. And with it we’re going to encounter yet another weird counting thing with indices. When we refer to the end of a slice we need to provide one index higher than the actual one we want. Something like this [start_index:(end_index + 1)]. This is called exclusive indexing because the value referred to after the : is excluded from the slice. Again, with time this will begin to make some kind of sense but for now we’ll just have to try and get used to it.

Let’s modify our script to pull out our middle two elements of our list.

# lists.py
my_list = [1, 2, "deepmay", 7.0]

print(f"The middle two elements are {my_list[1:3]}")
uv run lists.py
The middle two elements are [2, 'deepmay']

Couple of things to nitice here. First, notice that the printed slice just looks just like our printed list. That’s because it is another list! Also notice that we told Python to slice from index 1 to index 3 and as we discussed above, the value at index 3 was not included in the slice.

Often, we might want a slice that covers the beginning or end of our list, say if we want the first or last two elements. Python gives us short cuts for such slices. If we simply omit the start or end of a slice Python will infer that we want the slice to go to one of the ends.

Let’s add a couple more print statements to our script to see how this works. The first will retrieve all elements up to but excluding the the third element (2 in the index), the second will retrieve all elements from the third element to the end.

# lists.py
my_list = [1, 2, "deepmay", 7.0]

print(f"The middle two elements are {my_list[1:3]}")
print(f"The beginning slice is {my_list[:2]}")
print(f"The end slice is {my_list[2:]}")
uv run lists.py
The middle two elements are [2, 'deepmay']
The beginning slice is [1, 2]
The end slice is ['deepmay', 7.0]

Adding elements

Lists in Python are mutable, we can change them without having to recreate them. Most often, we’ll add items to a list using append(). This is a special kind of function that we haven’t seen yet called a method. A method is a function that belongs to some _object like a list or some other variable and does something with/to that object. Let’s use len() and append() in our script together to see what append does and how it’s different than a regular function like len()

# lists.py
my_list = [1, 2, "deepmay", 7.0]
print(f"Length before adding anything is {len(my_list)}")

my_list.append("hell yeah")
print(f"Length after adding a thing is {len(my_list)}")
uv run lists.py
Length before adding anything is 4
Length after adding a thing is 5

Before we return to the list of it all lets look at how our append() method works syntactically. Methods are said to belong to an object, in this case a list or specifically my_list.

Whether its a method or something we’ll touch on later called an attribute, we can access the thing that belong to an object using . like this object.thing_that_belongs_to_object.

Ok so, in the example above, we have an object (a list) called my_list which has a method called append() like every other list does. We access the method append() of my_list using the dot operator ..

The cool thing about methods, is that they are aware of the object they belong to. With len() we provide the list as an the argument but append() already knows all about our list. We only need to provide the thing we want to add. append() takes a single argument of any type and sticks it on the end of the list. What can sometimes be confusing is that append() does not return a list, or any value at all. Its like print() in that it just does a thing and doesn’t give anything back. Just to prove it, lets play around a little more with append.

# lists.py
my_list = [1, 2, "deepmay", 7.0]
print(f"Before we append we have {my_list}")

my_list.append("hell yeah")
print(f"After we append we have {my_list}")

my_list.append("hell")
my_list.append("fucken")
my_list(3)
my_list.append("yeah")
print(f"After we append a bunch more times we have {my_list}")

my_append = my_list.append(8)
print(f"Append returns nothing, see: {my_append}")
uv run lists.py
Before we append we have [1, 2, 'deepmay', 7.0]
After we append we have [1, 2, 'deepmay', 7.0, 'hell yeah']
After we append we have a bunch more times [1, 2, 'deepmay', 7.0, 'hell yeah', 'hell', 'fucken', 3, 'yeah']
Append returns nothing, see: None

Not exactly rocket science to do append() a handful of times and see that it really does work but append() is extremely useful so its worth ensuring that we really get it.

Removing elements

Most often if we need to remove an element, we’ll just slice out the part of the list we want to keep e.g. my_new_list = my_list[:-1] however, Python does provide list methods to remove elements which can be important in some circumstances. We will look at one such method, pop(). pop() is essentially the inverse of append. Without any argument provided, pop() will remove the last element of a list and return it. However, we could also supply an index to remove the element at a specific position.

# lists.py
my_list = [1, 2, "deepmay", 7.0]

my_list.pop()
print(f"List with the final element popped off: {my_list}")

# unlike append pop gives us a value back
my_popped_value = my_list.pop(0)
print(f"Popped value is {my_popped_value}")
print(f"List after additional pop: {my_list}")
uv run lists.py
List with final element popped off: [1, 2, 'deepmay']
Popped value is 1
List after additional pop: [2, 'deepmay']

While we might not need to pop() values terribly often, it comes up in some important algorithms such as breadth-first search.

Summary

To start this module we learned how to define a list using square brackets and values separated with commas. Next, we discussed indexing and slicing which will prove very useful throughout all of our Python experience. Indexing lets us grab and modify the individual values in a list and slicing let’s us easily take a subset. We ended with the two most common ways to add or remove elements of a list. It should be noted that there are others like extend() or insert() for adding to lists or remove() for removing elements.

Next time we will look at loops which will allow us to write scripts that don’t simply look line-by-line but rather repeat some set of lines over and over again.