Python Lists: How to Store a Hundred Things in One Variable
Indexing, slicing, adding, removing, sorting, plus the two sneaky bugs that catch every beginner.
Hey again! 👋
Confession time: I’ve been using lists on you for two posts now without properly introducing them.
Remember these from the loops post?
fruits = ["apple", "banana", "cherry"]
contacts = ["Alice", "Bob", "Charlie", "Diana", "Ethan"]We looped over them, searched through them, even built new ones with .append(). And it all just… worked. But we never stopped to ask: what actually is a list, and what else can it do?
Today we fix that.
Here’s why it matters. Imagine tracking marks for a class of 40 students. Without lists, you’d need mark1, mark2, mark3… all the way to mark40. Then try finding the highest one. 😵
With a list, it’s one variable and one line: max(marks).
Lists are probably the data structure you’ll use most in your entire Python life. Let’s get really comfortable with them.
What You’ll Master Today
By the end of this post, you’ll be able to:
✅ Create lists and access items by position (including from the end!)
✅ Slice out parts of a list
✅ Add, change, and remove items
✅ Sort lists, and avoid the None trap
✅ Use len, sum, min, max, and in like a pro
✅ Write your first list comprehension
✅ Build a working To-Do List app
Prerequisites: Everything so far, especially Loops and Functions. Today’s mini project leans on both.
Part 1: Creating Lists
A list is an ordered collection of items, written inside square brackets and separated by commas:
fruits = ["apple", "banana", "cherry"]
scores = [85, 92, 78]
mixed = ["Ajay", 25, True, 3.14]
empty = []Lists can hold anything: strings, numbers, booleans, even a mix. (In practice, most lists hold one type of thing. A list of scores, a list of names.)
Part 2: Indexing — Grabbing One Item
Every item in a list has a position number called an index. And just like range() from the loops post, counting starts at 0.
fruits = ["apple", "banana", "cherry"]
print(fruits[0])
print(fruits[1])
print(fruits[2])
# Output:
# apple
# banana
# cherryAsk for a position that doesn’t exist and Python complains:
print(fruits[3])
# IndexError: list index out of rangeThree items means indexes 0, 1, and 2. There is no index 3. You’ll see this error a lot while learning. Now you know exactly what it means.
Negative indexing: counting from the end
Here’s a Python trick people love:
print(fruits[-1])
print(fruits[-2])
# Output:
# cherry
# banana-1 is always the last item, -2 is second-to-last, and so on. No need to know how long the list is. Super handy.
Part 3: Slicing — Grabbing Many Items
Want a chunk of a list instead of one item? Use a slice: list[start:stop].
numbers = [10, 20, 30, 40, 50, 60]
print(numbers[1:4])
print(numbers[:3])
print(numbers[3:])
# Output:
# [20, 30, 40]
# [10, 20, 30]
# [40, 50, 60]Same rule as range(): the start is included, the stop is not. Leave out the start and it begins from the beginning; leave out the stop and it goes to the end.
Add a third number for the step:
print(numbers[::2])
print(numbers[::-1])
# Output:
# [10, 30, 50]
# [60, 50, 40, 30, 20, 10]That [::-1] reverses a list. It's one of the most-Googled Python tricks of all time, and now you don't need to Google it. 😎
Part 4: Changing Lists
Here’s a big difference between lists and strings: lists are mutable, which means you can change them after creating them.
fruits = ["apple", "banana", "cherry"]
fruits[1] = "blueberry"
print(fruits)
# Output:
# ['apple', 'blueberry', 'cherry']Try the same with a string and Python says no:
name = "Ajay"
name[0] = "B"
# TypeError: 'str' object does not support item assignmentAdding items
cart = ["milk", "bread"]
cart.append("eggs") # add to the end
print(cart)
cart.insert(0, "coffee") # add at a position
print(cart)
cart.extend(["rice", "salt"]) # add several at once
print(cart)
# Output:
# ['milk', 'bread', 'eggs']
# ['coffee', 'milk', 'bread', 'eggs']
# ['coffee', 'milk', 'bread', 'eggs', 'rice', 'salt']Removing items
cart = ["coffee", "milk", "bread", "eggs", "rice", "salt"]
cart.remove("bread") # remove by value
print(cart)
last = cart.pop() # remove and return the last item
print(last, cart)
first = cart.pop(0) # remove and return the item at index 0
print(first, cart)
del cart[1] # delete by index
print(cart)
# Output:
# ['coffee', 'milk', 'eggs', 'rice', 'salt']
# salt ['coffee', 'milk', 'eggs', 'rice']
# coffee ['milk', 'eggs', 'rice']
# ['milk', 'rice']Quick guide:
- Know the value? Use
.remove() - Know the position and want the item back? Use
.pop() - Just want it gone? Use
del
One warning: .remove() crashes if the item isn't there (ValueError: list.remove(x): x not in list). Check first with in:
if "bread" in cart:
cart.remove("bread")Part 5: Super-Useful List Tools
scores = [85, 92, 78, 92, 64]
print(len(scores)) # how many items
print(max(scores)) # highest
print(min(scores)) # lowest
print(sum(scores)) # total
print(sum(scores) / len(scores)) # average
print(scores.count(92)) # how many times 92 appears
print(scores.index(78)) # position of 78
print(92 in scores) # is 92 in the list?
# Output:
# 5
# 92
# 64
# 411
# 82.2
# 2
# 2
# TrueRemember writing the accumulator pattern to add numbers in the loops post? sum() does that for you in one word. It's still worth knowing how to write the loop yourself, because you'll need that pattern for things Python doesn't have a built-in for.
Sorting (and the None trap)
There are two ways to sort:
scores = [85, 92, 78, 92, 64]
scores.sort() # sorts the list itself
print(scores)
scores.sort(reverse=True) # biggest first
print(scores)
names = ["Zoe", "ajay", "Bob"]
print(sorted(names)) # returns a NEW sorted list
print(names) # original is untouched
# Output:
# [64, 78, 85, 92, 92]
# [92, 92, 85, 78, 64]
# ['Bob', 'Zoe', 'ajay']
# ['Zoe', 'ajay', 'Bob'](Notice 'ajay' came last? Python sorts uppercase letters before lowercase. Use sorted(names, key=str.lower) to sort them the way a human would.)
Get Ajay Narayan’s stories in your inbox
Join Medium for free to get updates from this writer.
Now the trap. Remember from the functions post that a function with no return gives back None? Well, .sort() changes the list in place and returns None:
scores = [85, 92, 78]
scores = scores.sort()
print(scores)
# Output:
# NoneOops, we just replaced our whole list with None. 😬 Rule: use .sort() on its own line, or use sorted() if you need a new list.
Part 6: The Copy Trap
This one confuses even people who’ve been coding for a while:
a = [1, 2, 3]
b = a
b.append(4)
print(a)
# Output:
# [1, 2, 3, 4]Wait, we changed b, so why did a change?!
Because b = a doesn't copy the list. It just gives the same list a second name. Think of it like two people sharing one Google Doc. Either of them edits, both see it.
If you want a real, separate copy:
a = [1, 2, 3]
b = a.copy()
b.append(4)
print(a)
print(b)
# Output:
# [1, 2, 3]
# [1, 2, 3, 4]Part 7: Looping Like a Pro
You already know for item in my_list. But what if you also need the position number? Use enumerate():
tasks = ["Study Python", "Buy groceries", "Call mom"]
for number, task in enumerate(tasks, start=1):
print(f"{number}. {task}")
# Output:
# 1. Study Python
# 2. Buy groceries
# 3. Call momenumerate hands you both the count and the item, and start=1 makes it count like a human instead of from 0.
Your first list comprehension
Remember the accumulator pattern for building a new list?
numbers = [1, 2, 3, 4, 5]
squares = []
for n in numbers:
squares.append(n * n)Python has a shortcut for exactly this, called a list comprehension:
numbers = [1, 2, 3, 4, 5]
squares = [n * n for n in numbers]
evens = [n for n in numbers if n % 2 == 0]
print(squares)
print(evens)
# Output:
# [1, 4, 9, 16, 25]
# [2, 4]Read it like a sentence: “give me n * n for each n in numbers." If it feels weird at first, that's normal. Use the regular loop until the shortcut feels natural. Both are correct.
Real-World: Class Marks Analyzer
marks = [72, 88, 45, 91, 60, 38, 79]
pass_mark = 40
passed = [m for m in marks if m >= pass_mark]
failed = [m for m in marks if m < pass_mark]
average = sum(marks) / len(marks)
print("Class Report")
print("-" * 40)
print(f"Students: {len(marks)}")
print(f"Highest: {max(marks)}")
print(f"Lowest: {min(marks)}")
print(f"Average: {average:.2f}")
print(f"Passed: {len(passed)}/{len(marks)}")
print(f"Failed marks: {failed}")
# Output:
# Class Report
# ----------------------------------------
# Students: 7
# Highest: 91
# Lowest: 38
# Average: 67.57
# Passed: 6/7
# Failed marks: [38]Try doing that with 7 separate variables. 😄
Mini Project: To-Do List Manager
Time to combine lists, loops, conditions, and functions into a real little app:
def show_tasks(tasks):
if not tasks:
print("\nNo tasks yet. Enjoy the free time! 🎉")
return
print("\nYour tasks:")
for number, task in enumerate(tasks, start=1):
print(f" {number}. {task}")
def add_task(tasks):
task = input("New task: ").strip()
if task:
tasks.append(task)
print(f"Added: {task}")
else:
print("Task can't be empty.")
def complete_task(tasks):
show_tasks(tasks)
if not tasks:
return
number = int(input("Task number to complete: "))
if 1 <= number <= len(tasks):
done = tasks.pop(number - 1)
print(f"Completed: {done} ✅")
else:
print("That task number doesn't exist.")
def main():
tasks = []
print("=" * 50)
print("TO-DO LIST MANAGER")
print("=" * 50)
while True:
print("\n1. View tasks")
print("2. Add task")
print("3. Complete task")
print("4. Quit")
choice = input("Choose (1-4): ")
if choice == "1":
show_tasks(tasks)
elif choice == "2":
add_task(tasks)
elif choice == "3":
complete_task(tasks)
elif choice == "4":
print(f"Goodbye! You have {len(tasks)} task(s) left.")
break
else:
print("Invalid choice. Try again.")
main()A few things worth noticing:
if not tasks:An empty list counts asFalse, so this reads as "if there are no tasks." Very Pythonic.tasks.pop(number - 1)Users count from 1, Python counts from 0, so we subtract 1.- The functions change
taskswithout returning it. Remember the copy trap? When you pass a list into a function, the function gets the same list, not a copy. Soadd_task(tasks)updates the real list inmain(). That's mutability working for you this time.
Practice Challenges
Beginner Level
- Favorite Movies: Make a list of 5 movies. Print the first one, the last one (using a negative index), and how many there are.
- Shopping List: Start with an empty list. Add 3 items with
.append(), remove one, then print the list sorted.
Intermediate Level
- No Duplicates: Given
[3, 1, 3, 2, 1, 5], build a new list with duplicates removed while keeping the original order. (Hint: loop +in+.append().) - Second Largest: Find the second-largest number in a list without using
.sort()orsorted().
Advanced Level
- To-Do List 2.0: Upgrade the mini project. Add an option to edit a task, and keep a second list of completed tasks you can view at the end.
Before You Go
Did lists click for you? Hit that 👏 clap button.
Built the To-Do app? Tell me in the comments what feature you added. Bonus points if it’s something I didn’t think of.
Coming up next: Dictionaries, where we stop storing things by position and start storing them by name. Your contact book is about to get phone numbers. 📱 Hit Follow so you don’t miss it.
See you in the next post! 🐍✨
Enjoying this series? Share it with a friend learning Python. Let’s all grow faster, together.
Check out the series so far:
- Python in 2025: Why This Language Still Dominates
- Variables and Data Types
- Python Operators Decoded
- Control Flow Mastery
- Why Write Code Once When It Can Just… Repeat? (Loops)
- Stop Copy-Pasting Your Code (Functions)





