Why Does list[5] Crash? Python Lists From Zero to Confident
š Full written solution: https://timepasshub2539-hue.github.io/leetcode-python/python-lesson-08-lists.html š» Code on GitHub: https://github.com/timepasshub2539-hue/leetcode-python/tree/main/python-lesson-08-lists Chapters: 0:00 Python Lesson 8: Lists 0:19 What is a list? 0:40 Indexing: grab one item 1:09 Slicing: grab a chunk 1:33 Lists can hold anything 1:59 Lists are mutable 2:19 Adding items 2:42 Removing items 3:04 Handy list methods 3:24 Mistake: the copy trap 3:50 Mistake: index out of range 4:13 List vs tuple 4:41 Recap In Lesson 8 you'll learn what Python lists are and why they're ordered, how zero-based and negative indexing work, and how to slice out any chunk you want. We mutate lists by adding and removing items, tour the handy built-in list methods, and fix the two mistakes that trip everyone up: index out of range and copy-vs-reference. Finish with a clear list-vs-tuple breakdown and a quick recap. Perfect if you're learning Python from scratch and want lists to finally click. #Python #LearnPython #PythonForBeginners #Coding #PythonLists Watch next: - Python Basics: Variables, Loops, Lists & Functions Explained Simply: https://youtu.be/VnyBhaHk1bA - Find Greatest Common Divisor of Array ā LeetCode Daily Python Solution (the trick nobody sees): https://youtu.be/brzex76x6rU - Sorted GCD Pair Queries ā LeetCode Daily Python Solution (5 Billion Pairs, No List): https://youtu.be/JVLzC_apwYQ
8. Introduction
If you write Python for more than an afternoon, you will reach for a list. It is the container you will use to hold search results, rows from a database, lines from a file, coordinates, user records ā anything that arrives as "more than one of something." Lists are the workhorse of the language, and getting comfortable with them early pays off every single day after.
This lesson matters for two reasons. First, almost every Python coding interview leans on lists. Interviewers use them to test whether you understand indexing, whether you know that slicing creates a new object, and whether you grasp the difference between a reference and a copy. These are not trivia questions ā they reveal how well you understand memory, mutation, and data structures in general. Second, two of the most common bugs in all of programming ā off-by-one index errors and accidental shared references ā both live right here in list territory. Learn them once, on purpose, and you will stop shipping them by accident.
By the end of this article you will be able to build a list, grab any single item, slice out a range, mutate elements in place, and choose the right method to grow or shrink your data. We will also break down list versus tuple so you always pick the correct tool.
9. Problem Overview
Here is the core challenge, stated plainly: you have many pieces of related data, and you need a single structure that keeps them in order, lets you read any one of them instantly, and lets you change the collection as your program runs.
A Python list solves exactly this. It is an ordered collection of items wrapped in square brackets and separated by commas. "Ordered" means the items stay in the sequence you placed them ā Python does not shuffle them behind your back. Each item lives in a numbered slot, and you can:
- Read a single item by its slot number (indexing)
- Read a range of items at once (slicing)
- Change items after the list is created (mutation)
- Grow or shrink the list with built-in methods
The mental model that sticks: picture an egg carton. One container, many slots, each slot holding one item, all in a fixed row.
10. Example
Let's start with a small list and see what each operation returns.
fruits = ["apple", "banana", "plum"]
print(fruits[0]) # apple -> first slot
print(fruits[2]) # plum -> third slot
print(fruits[-1]) # plum -> last slot
Notice something important: fruits[2] and fruits[-1] both return "plum". That is not a coincidence. Because there are three items, the last one sits at index 2 and at index -1. Positive indexes count forward from the start; negative indexes count backward from the end.
Now slicing:
numbers = [10, 20, 30, 40, 50]
print(numbers[1:4]) # [20, 30, 40]
Read that carefully. We asked for 1:4, but we got slots 1, 2, and 3 ā not slot 4. The stop number is exclusive: the slice runs up to but does not touch it. This single rule trips up more beginners than any other.
11. Intuition
Before touching methods, an experienced engineer builds a mental picture of how the data is laid out in memory, because every operation follows naturally from that picture.
Think about why indexing starts at zero instead of one. An index is really an offset ā a measure of "how far from the beginning." The first item is zero steps from the start, the second is one step away, and so on. Once you see indexes as distances rather than counts, negative indexing clicks too: -1 means "one step back from the end."
That same offset thinking explains slicing. When you write start:stop, you are describing a half-open range: include start, walk forward, stop just before stop. Why exclusive? Because it makes the math clean ā the number of items in a[start:stop] is simply stop - start. numbers[1:4] gives 4 - 1 = 3 items. No mental gymnastics.
Finally, the intuition that separates a shaky beginner from a confident developer: a variable name is a label, not a box. When you assign one list to another name, you are hanging a second label on the same carton ā not copying the eggs. Hold that idea and the "copy trap" we cover later will never bite you.
12. Brute Force Approach (The Manual Way)
Suppose you want to combine two lists, or find where a value lives. A beginner often does it the long, manual way.
Idea: loop through everything by hand.
# Combine two lists the manual way
a = [1, 2, 3]
b = [4, 5, 6]
for item in b:
a.append(item) # add one at a time
# a is now [1, 2, 3, 4, 5, 6]
Advantages:
- It works, and it is explicit ā you can see every step.
- Good for learning what is happening under the hood.
Disadvantages:
- More lines than necessary.
- Easy to introduce an off-by-one or logic slip.
- Slower to read and reason about.
Time complexity: O(n) for the loop.
Space complexity: O(1) extra, since we mutate a in place.
This is fine, but Python gives us cleaner tools.
13. Optimal Approach (Use the Right Built-in)
The optimal move is to let Python's built-in methods do the work. Here is the full toolkit, grouped by what it does.
Growing a list:
| Method | What it does | Analogy |
|---|---|---|
append(x) |
Adds one item to the end | Adding one car to the back of a train |
insert(i, x) |
Wedges an item at slot i, shifting the rest right |
Cutting into a line |
extend(other) |
Pours an entire other list onto the end | Merging two trains |
The manual loop above collapses to a single line:
a.extend(b) # one line replaces the whole loop
extend is genuinely underrated ā many developers write a loop with append when one extend reads cleaner and runs just as fast.
Shrinking a list:
| Method | What it does | Key detail |
|---|---|---|
remove(x) |
Deletes the first matching value | Ignores later duplicates |
pop(i) |
Removes the item at slot i and returns it |
Great when you need the removed value |
Reading and reordering:
| Method | What it does |
|---|---|
sort() |
Reorders in place, smallest to largest |
reverse() |
Flips the current order |
index(x) |
Tells you which slot a value lives in |
len(list) |
Counts the top-level slots |
14. Python Code
# --- Building and reading ---
fruits = ["apple", "banana", "plum"]
first = fruits[0] # "apple"
last = fruits[-1] # "plum"
middle = fruits[1:2] # ["banana"] (slice returns a new list)
# --- Mutating in place ---
colors = ["red", "green", "blue"]
colors[1] = "yellow" # ["red", "yellow", "blue"]
# --- Growing ---
colors.append("purple") # add one to the end
colors.insert(0, "black") # wedge at the front
colors.extend(["white", "gray"]) # pour in a whole list
# --- Shrinking ---
colors.remove("black") # delete first "black"
popped = colors.pop() # remove + return last item
# --- Reordering and searching ---
scores = [30, 10, 20]
scores.sort() # [10, 20, 30]
scores.reverse() # [30, 20, 10]
where = scores.index(20) # 1
# --- Copying safely ---
original = [1, 2, 3]
clone = original.copy() # a real, separate list
clone.append(4) # original is untouched
15. Code Walkthrough
fruits[0]andfruits[-1]show forward and backward indexing on the same list.fruits[1:2]returns a new one-element list ā slicing never mutates the original, it produces a fresh copy of that range.colors[1] = "yellow"proves lists are mutable: we rewrite a slot in place, no new list required. (A string would refuse this ā strings are frozen once created.)append,insert, andextendeach grow the list differently: one item at the end, one item at a position, or a whole list poured in.pop()with no argument removes and returns the last item, which is why we capture it inpopped.original.copy()is the hero of the section ā it creates a genuinely separate list so mutatingcloneleavesoriginalalone.
16. Dry Run
Let's trace the shrinking and reordering steps on one list.
Start: colors = ["red", "green", "blue"]
| Step | Operation | Result |
|---|---|---|
| 1 | colors[1] = "yellow" |
["red", "yellow", "blue"] |
| 2 | colors.append("purple") |
["red", "yellow", "blue", "purple"] |
| 3 | colors.insert(0, "black") |
["black", "red", "yellow", "blue", "purple"] |
| 4 | colors.remove("black") |
["red", "yellow", "blue", "purple"] |
| 5 | colors.pop() |
returns "purple", list = ["red", "yellow", "blue"] |
Every operation reshaped the same list in place ā no copies were made along the way.
17. Complexity Analysis
| Operation | Time | Why |
|---|---|---|
Index list[i] |
O(1) | Direct jump to a slot by offset |
Slice list[a:b] |
O(k) | Copies k items into a new list |
append(x) |
O(1)* | Adds to the end (amortized) |
insert(i, x) |
O(n) | Must shift everything after i |
remove(x) |
O(n) | Scans for the value, then shifts |
pop() (end) |
O(1) | Removes the last slot |
pop(i) (middle) |
O(n) | Shifts everything after i |
sort() |
O(n log n) | Timsort under the hood |
Space: most in-place methods use O(1) extra memory. Slicing and copy() use O(n) because they build a new list.
The takeaway: appending and end-popping are cheap; inserting and removing from the middle are not, because Python has to shuffle every later item over.
18. Alternative Solutions
Slicing to copy. Instead of original.copy(), you will often see clone = original[:]. A full slice from start to end produces a new list, so it behaves identically for a flat list. Both are O(n).
clone = original[:] # same effect as original.copy()
Tuples for fixed data. If your collection should never change ā a coordinate, an RGB value, a database key ā reach for a tuple instead.
| List | Tuple | |
|---|---|---|
| Syntax | [1, 2, 3] |
(1, 2, 3) |
| Changeable? | Yes (mutable) | No (immutable) |
| Best for | Data that grows/shrinks | Fixed, protected data |
| Upside | Rich editing methods | Safety + slightly faster |
| Downside | Slightly slower, mutable | No editing methods |
Use a list for a shopping cart. Use a tuple for a point on a map that should never wobble.
19. Edge Cases
- Empty list:
[]is valid.len([])is0, and any index raises an error. - Index out of range: a three-item list has a highest valid index of
2. Asking for index3raisesIndexError. - Duplicates:
remove(x)deletes only the first match ā later copies survive. - Negative index past the start:
fruits[-99]on a small list also raisesIndexError. - Slicing beyond bounds: unlike indexing,
numbers[1:999]does not error ā it just returns whatever exists. Slicing is forgiving; indexing is not. - Nested lists:
len()only counts top-level slots, solen([1, [2, 3]])is2, not3.
20. Common Mistakes
- Off-by-one at the end. With three items, the last index is
2, not3. Reaching for3throwsIndexError. Whenever you index the end, pause and subtract one. - The copy trap. Writing
b = adoes not create a second list ā both names point at the same carton, so editingbsilently wrecksa. Usea.copy()ora[:]when you need a true clone. - Forgetting the exclusive stop in slices.
numbers[1:4]gives three items, not four. The stop is never included. - Assuming
removedeletes all matches. It only kills the first one. To remove every occurrence, you need a loop or a comprehension. - Expecting
sort()to return a new list.sort()returnsNoneand mutates in place. If you writex = mylist.sort(),xisNone. Usesorted(mylist)when you want a new list back.
21. Interview Questions
- What is the difference between
appendandextend? (One adds a single item; the other merges an entire iterable.) - How does
b = adiffer fromb = a.copy()? (Shared reference versus independent list.) - Why does slicing not raise an error when the range exceeds the list length, but indexing does?
- What does
list.sort()return, and how is it different fromsorted(list)? - When would you choose a tuple over a list?
- What is the time complexity of inserting at the front of a list, and why?
22. Similar Problems
- Two Sum ā the classic list-plus-lookup problem; understanding indexing is step one.
- Remove Duplicates from Sorted Array ā hinges on in-place mutation and index management.
- Merge Sorted Array ā tests
extend, slicing, and in-place edits. - Rotate Array ā pure slicing and reindexing practice.
Each of these rewards the exact skills in this lesson: knowing where items live, how ranges work, and when you are mutating versus copying.
23. Key Takeaways
- A list is an ordered, mutable collection in square brackets.
- Indexing starts at zero;
-1is always the last item. - Slicing
start:stopis half-open ā the stop is excluded. - Grow with
append,insert,extend; shrink withremove(by value) andpop(by position, returns the item). =shares a reference;.copy()(or[:]) clones.- The last valid index is always
len(list) - 1.
24. Watch the Video
Reading builds understanding, but watching the operations reshape a list in real time makes it stick. See every method demonstrated live, including the copy trap in action: Watch the full lesson here.
25. About the Series
This is part of the Daily Python & LeetCode series ā short, focused lessons that teach one Python or algorithms concept at a time, from absolute fundamentals up to interview-grade problem solving. Each entry pairs clear intuition with production-quality code so you learn why an approach works, not just how to type it. Follow along in order and you build a genuine foundation in data structures, algorithms, and software engineering.
26. Call To Action
If this helped lists finally click, here is how to keep the momentum going:
- Subscribe on YouTube so you never miss a lesson.
- Subscribe to the Substack for the full written breakdowns.
- Comment with the concept that used to confuse you most.
- Share this with someone who is learning Python right now.
The solution
a = [1, 2, 3]
b = a # same list!
b.append(4)
print(a) # [1, 2, 3, 4]
c = a.copy() # real copyReady to try it yourself? Solve General problems with instant feedback in the practice sandbox.
Practice now ā


