Home Python Python Lists Explained — Creation, Indexing, Methods and Common Mistakes

Python Lists Explained — Creation, Indexing, Methods and Common Mistakes

In Plain English 🔥
Imagine you're writing a shopping list on paper — milk, eggs, bread, butter. A Python list is exactly that: an ordered collection of items you can add to, cross off, rearrange, or read back at any time. The difference is your paper list can't automatically count itself, sort itself alphabetically, or tell you instantly whether 'eggs' is on it. A Python list can do all of that in a single line of code. That's the superpower we're handing you today.
⚡ Quick Answer
Imagine you're writing a shopping list on paper — milk, eggs, bread, butter. A Python list is exactly that: an ordered collection of items you can add to, cross off, rearrange, or read back at any time. The difference is your paper list can't automatically count itself, sort itself alphabetically, or tell you instantly whether 'eggs' is on it. A Python list can do all of that in a single line of code. That's the superpower we're handing you today.

Every real program manages collections of things. A music app tracks playlists. A banking app tracks transactions. A weather app tracks seven days of temperatures. Without a way to group related data together, you'd need to create a separate variable for every single item — temperature_monday, temperature_tuesday, temperature_wednesday — and your code would collapse under its own weight before it ever did anything useful. Lists are Python's answer to this problem, and they're one of the first tools every professional Python developer reaches for.

What Is a Python List and How Do You Create One?

A list in Python is an ordered, mutable collection that can hold any number of items. 'Ordered' means the items stay in the exact sequence you put them in — item 1 is always item 1. 'Mutable' means you can change the list after you create it — add items, remove items, swap them around. That flexibility is what makes lists so useful in everyday programming.

You create a list with square brackets []. Put your items inside, separated by commas. Python doesn't care what those items are — they can be numbers, text, other lists, or a wild mix of all three. Unlike some other languages, Python lists don't force you to declare a fixed size up front. You start small and grow as needed, like a shopping trolley you can always add more items to.

The empty list [] is completely valid and very common — you'll often create an empty list first and then fill it up as your program runs. Think of it as grabbing an empty basket before you start shopping.

creating_lists.py · PYTHON
1234567891011121314151617181920212223242526
# --- Creating different kinds of lists ---

# A list of grocery items (strings)
grocery_list = ["milk", "eggs", "bread", "butter"]

# A list of daily temperatures in Celsius (floats)
weekly_temps = [18.5, 21.0, 19.3, 22.7, 20.1, 17.8, 23.4]

# A list of student ages (integers)
student_ages = [14, 15, 15, 16, 14, 17]

# A mixed list — Python allows different types in one list
user_profile = ["Alice", 30, True, 4.9]

# An empty list — created first, filled later
pending_tasks = []

# Print each list to see exactly what Python stores
print("Grocery list:", grocery_list)       # shows all 4 items in order
print("Weekly temps:", weekly_temps)       # shows all 7 temperatures
print("Student ages:", student_ages)       # shows 6 ages
print("User profile:", user_profile)       # shows mixed types
print("Pending tasks:", pending_tasks)     # shows empty brackets

# Check how many items are in a list using len()
print("Number of groceries:", len(grocery_list))   # len() counts the items
▶ Output
Grocery list: ['milk', 'eggs', 'bread', 'butter']
Weekly temps: [18.5, 21.0, 19.3, 22.7, 20.1, 17.8, 23.4]
Student ages: [14, 15, 15, 16, 14, 17]
User profile: ['Alice', 30, True, 4.9]
Pending tasks: []
Number of groceries: 4
⚠️
Pro Tip:Use `len(your_list)` any time you need to know how many items are in a list. It works on strings, tuples and dictionaries too — one function to count them all.

Accessing Items with Indexing and Slicing

Every item in a list has an address called an index. Python starts counting from 0, not 1 — so the first item is at index 0, the second at index 1, and so on. This trips up nearly every beginner at least once, so burn this into your memory: first item = index 0.

Python also gives you negative indexing, which is genuinely clever. Index -1 always refers to the last item, -2 to the second-to-last, and so on. This means you never need to calculate len(list) - 1 just to grab the last element — -1 does it instantly.

Slicing lets you extract a portion of a list using the syntax list[start:stop]. The slice starts at the start index and goes up to — but does NOT include — the stop index. It's like saying 'give me items from position 1 up to, but not including, position 4'. You can also add a step value: list[start:stop:step] to skip every other item, reverse the list, and more. Slicing always returns a brand new list — it doesn't modify the original.

list_indexing_and_slicing.py · PYTHON
12345678910111213141516171819202122232425262728293031323334
# Our list of planet names in order from the sun
planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
#  indexes:      0         1        2       3        4          5         6         7
# neg indexes:  -8        -7       -6      -5       -4         -3        -2        -1

# --- Positive indexing ---
print(planets[0])    # First planet  → Mercury
print(planets[2])    # Third planet  → Earth
print(planets[7])    # Last planet   → Neptune

# --- Negative indexing (count from the end) ---
print(planets[-1])   # Last planet         → Neptune
print(planets[-2])   # Second to last      → Uranus
print(planets[-8])   # Same as index 0     → Mercury

# --- Slicing: list[start:stop] — stop is EXCLUDED ---
inner_planets = planets[0:4]        # indexes 0, 1, 2, 3 (not 4)
print("Inner planets:", inner_planets)

outer_planets = planets[4:]         # from index 4 to the end
print("Outer planets:", outer_planets)

first_three = planets[:3]           # from the start up to (not including) index 3
print("First three:", first_three)

# --- Slicing with a step ---
every_other = planets[::2]          # start to end, jumping 2 at a time
print("Every other planet:", every_other)

reversed_planets = planets[::-1]    # step of -1 reverses the whole list
print("Reversed:", reversed_planets)

# --- Slicing creates a NEW list — original is unchanged ---
print("Original still intact:", planets)
▶ Output
Mercury
Earth
Neptune
Neptune
Uranus
Mercury
Inner planets: ['Mercury', 'Venus', 'Earth', 'Mars']
Outer planets: ['Jupiter', 'Saturn', 'Uranus', 'Neptune']
First three: ['Mercury', 'Venus', 'Earth']
Every other planet: ['Mercury', 'Earth', 'Jupiter', 'Uranus']
Reversed: ['Neptune', 'Uranus', 'Saturn', 'Jupiter', 'Mars', 'Earth', 'Venus', 'Mercury']
Original still intact: ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']
⚠️
Watch Out:Accessing an index that doesn't exist — like `planets[10]` on an 8-item list — raises an `IndexError`. Always make sure your index is between 0 and `len(list) - 1`, or use negative indexes to count from the end safely.

Modifying Lists — The Essential Built-in Methods

Because lists are mutable, Python gives you a rich toolkit for changing them. These built-in methods are like the tools on a Swiss Army knife — each one does a specific job cleanly.

append(item) adds one item to the end. insert(index, item) drops an item at a specific position, pushing everything else right. extend(another_list) merges another list onto the end — different from append, which would nest the whole second list as a single item inside the first.

For removing items: remove(item) deletes the first occurrence of a specific value. pop(index) removes the item at a given index and returns it to you — useful when you want to both remove and use the item at the same time. pop() with no argument removes the last item. clear() wipes the entire list.

For organising: sort() rearranges items in ascending order in place (it modifies the original list). reverse() flips the order in place. sorted() is a built-in function — not a method — that returns a new sorted list without touching the original.

Use index(item) to find where something lives in the list, and count(item) to see how many times something appears.

list_methods.py · PYTHON
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
# Start with a list of tasks in a to-do app
tasks = ["buy groceries", "call dentist", "write report", "call dentist"]

# --- append() — add one item to the end ---
tasks.append("pay electricity bill")   # adds to the tail of the list
print("After append:", tasks)

# --- insert() — add at a specific position ---
tasks.insert(1, "reply to emails")     # inserts at index 1, shifts others right
print("After insert at index 1:", tasks)

# --- extend() — merge another list onto the end ---
extra_tasks = ["book flight", "renew passport"]
tasks.extend(extra_tasks)              # each item of extra_tasks is added individually
print("After extend:", tasks)

# --- remove() — delete the first matching value ---
tasks.remove("call dentist")           # removes only the FIRST occurrence
print("After remove 'call dentist':", tasks)

# --- pop() — remove and RETURN the item at an index ---
finished_task = tasks.pop(0)           # removes the item at index 0 and gives it back
print("Popped task:", finished_task)
print("List after pop:", tasks)

# --- count() — how many times does a value appear? ---
duplicate_count = tasks.count("call dentist")   # still one 'call dentist' left
print("'call dentist' appears:", duplicate_count, "time(s)")

# --- index() — find the position of a value ---
position = tasks.index("write report")
print("'write report' is at index:", position)

# --- sort() — sorts the list IN PLACE (modifies original) ---
fruit_prices = [3.50, 1.20, 4.75, 2.00, 0.99]
fruit_prices.sort()                    # ascending order by default
print("Sorted prices:", fruit_prices)

# --- sorted() — returns a NEW sorted list, original untouched ---
unsorted_names = ["Zara", "Alice", "Mike", "Beth"]
alpha_names = sorted(unsorted_names)   # original list not changed
print("Sorted names (new list):", alpha_names)
print("Original names unchanged:", unsorted_names)

# --- reverse() — flip the list IN PLACE ---
fruit_prices.reverse()
print("Reversed prices:", fruit_prices)

# --- clear() — empty the entire list ---
temporary_cache = ["data1", "data2", "data3"]
temporary_cache.clear()
print("Cache after clear:", temporary_cache)
▶ Output
After append: ['buy groceries', 'call dentist', 'write report', 'call dentist', 'pay electricity bill']
After insert at index 1: ['buy groceries', 'reply to emails', 'call dentist', 'write report', 'call dentist', 'pay electricity bill']
After extend: ['buy groceries', 'reply to emails', 'call dentist', 'write report', 'call dentist', 'pay electricity bill', 'book flight', 'renew passport']
After remove 'call dentist': ['buy groceries', 'reply to emails', 'write report', 'call dentist', 'pay electricity bill', 'book flight', 'renew passport']
Popped task: buy groceries
List after pop: ['reply to emails', 'write report', 'call dentist', 'pay electricity bill', 'book flight', 'renew passport']
'call dentist' appears: 1 time(s)
'write report' is at index: 1
Sorted prices: [0.99, 1.2, 2.0, 3.5, 4.75]
Sorted names (new list): ['Alice', 'Beth', 'Mike', 'Zara']
Original names unchanged: ['Zara', 'Alice', 'Mike', 'Beth']
Reversed prices: [4.75, 3.5, 2.0, 1.2, 0.99]
Cache after clear: []
🔥
Interview Gold:`sort()` modifies the list in place and returns `None`. `sorted()` returns a new list and leaves the original alone. Interviewers love this distinction. If you write `my_list = my_list.sort()`, you'll get `None` assigned to `my_list` — a classic bug.

Looping Through Lists and List Comprehensions

Looping is where lists go from a storage box to a powerhouse. The for loop in Python is designed to walk through any list naturally — you don't need to manage an index counter or worry about going out of bounds. Python handles all of that for you.

When you need both the index and the value at the same time — say, to number your output — use enumerate(). It hands you both on every iteration as a pair, which is far cleaner than manually tracking a counter variable.

List comprehensions are Python's most elegant feature for creating a new list from an existing one in a single, readable line. The pattern is [expression for item in list]. You can also add a condition: [expression for item in list if condition]. Once it clicks, you'll reach for it constantly.

Under the hood, a list comprehension is equivalent to a for loop that appends to a new list — but it's faster and more Pythonic. If you're building a new list by transforming or filtering another list, a comprehension is almost always the right choice.

looping_and_comprehensions.py · PYTHON
1234567891011121314151617181920212223242526272829303132333435363738
# A list of exam scores for a class
exam_scores = [72, 85, 91, 60, 78, 95, 55, 88, 74, 66]

# --- Basic for loop --- iterate over every score
print("All scores:")
for score in exam_scores:
    print(" ", score)   # each score printed on its own line

# --- enumerate() — gives you index AND value together ---
print("\nScores with student numbers:")
for student_number, score in enumerate(exam_scores, start=1):  # start=1 so counting begins at 1
    print(f"  Student {student_number}: {score}")

# --- List comprehension: transform every item ---
# Give every student a 5-point bonus
boosted_scores = [score + 5 for score in exam_scores]
print("\nBoosted scores:", boosted_scores)

# --- List comprehension: filter items with a condition ---
# Only keep scores that are a pass (70 or above)
passing_scores = [score for score in exam_scores if score >= 70]
print("Passing scores:", passing_scores)

# --- List comprehension: transform AND filter together ---
# Boost only failing scores (below 70) by 10 points
adjusted_scores = [score + 10 if score < 70 else score for score in exam_scores]
print("Adjusted scores:", adjusted_scores)

# --- Equivalent for loop (to show what comprehension does under the hood) ---
adjusted_scores_loop = []
for score in exam_scores:
    if score < 70:
        adjusted_scores_loop.append(score + 10)   # failing: add 10
    else:
        adjusted_scores_loop.append(score)         # passing: keep as-is

print("Same result via loop:", adjusted_scores_loop)
print("Both methods match:", adjusted_scores == adjusted_scores_loop)  # should be True
▶ Output
All scores:
72
85
91
60
78
95
55
88
74
66

Scores with student numbers:
Student 1: 72
Student 2: 85
Student 3: 91
Student 4: 60
Student 5: 78
Student 6: 95
Student 7: 55
Student 8: 88
Student 9: 74
Student 10: 66

Boosted scores: [77, 90, 96, 65, 83, 100, 60, 93, 79, 71]
Passing scores: [72, 85, 91, 78, 95, 88, 74]
Adjusted scores: [72, 85, 91, 70, 78, 95, 65, 88, 74, 76]
Same result via loop: [72, 85, 91, 70, 78, 95, 65, 88, 74, 76]
Both methods match: True
⚠️
Pro Tip:List comprehensions are not just shorter — they're measurably faster than equivalent `for` loops with `.append()` because Python optimises them internally. For transforming or filtering collections, always prefer a comprehension over a manual loop.
Method / ActionModifies Original?ReturnsBest Used When
append(item)YesNoneAdding a single item to the end
extend(list)YesNoneMerging another list onto the end
insert(i, item)YesNoneAdding an item at a specific position
remove(item)YesNoneDeleting the first match by value
pop(index)YesThe removed itemRemoving and using the item at once
sort()Yes (in place)NonePermanently sorting the original list
sorted(list)NoNew sorted listSorting without changing the original
list[start:stop]NoNew list (slice)Extracting a portion of a list

🎯 Key Takeaways

  • Python list indexing starts at 0, not 1 — the first item is always list[0], and the last is always list[-1] regardless of how long the list is.
  • sort() permanently reorders the original list and returns None — never assign its return value. Use sorted() when you need a new sorted list without touching the original.
  • Slicing (list[start:stop]) always returns a brand-new list — it never modifies the original, making it safe for creating subsets without side effects.
  • List comprehensions ([expr for item in list if condition]) are the Pythonic way to build new lists from existing ones — they're cleaner and faster than equivalent for + append loops.

⚠ Common Mistakes to Avoid

  • Mistake 1: Using my_list = my_list.sort().sort() sorts in place and returns None, so you're assigning None to your variable and losing the list entirely. Fix: just call my_list.sort() on its own line, or use my_list = sorted(my_list) if you need the result assigned.
  • Mistake 2: Modifying a list while looping over it — removing items inside a for loop causes Python to skip items silently because the indexes shift mid-loop. Fix: loop over a copy (for item in my_list[:]) or collect items to remove and delete them after the loop finishes.
  • Mistake 3: Confusing append() and extend() — calling tasks.append(["a", "b"]) adds the entire list as a single nested item: [..., ['a', 'b']]. If you want the items merged in individually, use tasks.extend(["a", "b"]) which gives [..., 'a', 'b'].

Interview Questions on This Topic

  • QWhat is the difference between `sort()` and `sorted()` in Python, and when would you choose one over the other?
  • QHow does Python list indexing work, and what is the index of the last element in a list of 5 items if you use both positive and negative indexing?
  • QIf you do `list_b = list_a` and then modify `list_b`, does `list_a` change too — and why? How would you make a true independent copy?

Frequently Asked Questions

Can a Python list store different data types at the same time?

Yes — Python lists are heterogeneous, meaning a single list can hold strings, integers, floats, booleans, or even other lists all at once. For example, profile = ['Alice', 30, True, 4.9] is perfectly valid. That said, mixing types can make sorting or processing harder, so use it when it genuinely makes sense.

What is the difference between a Python list and a tuple?

Both are ordered sequences, but a list is mutable (you can change it after creation) while a tuple is immutable (once created, it cannot be modified). Lists use square brackets [] and tuples use parentheses (). Use a tuple when you have data that should never change — like GPS coordinates or RGB colour values.

How do I check if an item exists in a Python list?

Use the in keyword — it returns True or False. For example: if 'eggs' in grocery_list: checks cleanly without needing a loop. For the opposite check, use not in: if 'butter' not in grocery_list:. This is the idiomatic Python way and is very readable.

🔥
TheCodeForge Editorial Team Verified Author

Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.

← Previousmatch-case Statement in Python 3.10Next →Tuples in Python
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged