Python

Python Fundamentals

Lists

A list is an ordered, mutable collection that can hold items of any type — even mixed types.

JrCodex·6 min read

Jr Codex Python Notes

Level: Beginner Prerequisites: Chapter 5 Time to complete: ~25 minutes


Table of Contents

  1. What is a List?
  2. Indexing & Slicing
  3. Lists Are Mutable
  4. Common List Methods
  5. Copying Lists — a Common Trap
  6. Nested Lists
  7. Sorting
  8. Summary & Next Steps

1. What is a List?

A list is an ordered, mutable collection that can hold items of any type — even mixed types.

fruits = ["apple", "banana", "cherry"]
numbers = [1, 2, 3, 4, 5]
mixed = ["Alice", 25, True, 3.14]
empty = []
 
print(len(fruits))     # 3

2. Indexing & Slicing

Lists use the same indexing/slicing rules as strings (Chapter 4) — no coincidence, both are sequences.

fruits = ["apple", "banana", "cherry", "date"]
 
print(fruits[0])        # 'apple'
print(fruits[-1])        # 'date'
print(fruits[1:3])       # ['banana', 'cherry']
print(fruits[::-1])      # ['date', 'cherry', 'banana', 'apple'] — reversed

3. Lists Are Mutable

Unlike strings, lists can be changed in place — this is the key distinction to internalize.

fruits = ["apple", "banana", "cherry"]
fruits[0] = "avocado"          # ✓ allowed — strings would raise TypeError here
print(fruits)                   # ['avocado', 'banana', 'cherry']
 
fruits.append("date")           # add to the end
fruits.insert(1, "blueberry")   # insert at index 1
fruits.remove("banana")         # remove by value
popped = fruits.pop()           # remove & return last item
del fruits[0]                   # remove by index
Mutability Matters
─────────────────────────────────────────
  a = [1, 2, 3]
  b = a          ← b points to the SAME list object as a
  b.append(4)
  print(a)       ← [1, 2, 3, 4]  — a changed too! Same object.
─────────────────────────────────────────

This is the same identity-vs-equality concept from Chapter 3 (is vs ==), now with real consequences.


4. Common List Methods

nums = [3, 1, 4, 1, 5, 9, 2, 6]
 
nums.append(10)          # add to end       → [3,1,4,1,5,9,2,6,10]
nums.extend([7, 8])      # add multiple      → [...,10,7,8]
nums.insert(0, 100)      # insert at index    → [100,3,1,4,...]
nums.remove(1)           # remove FIRST '1'   → removes one occurrence
nums.pop()                # remove & return last item
nums.pop(0)                # remove & return item at index 0
nums.count(1)              # how many times '1' appears
nums.index(4)               # index of first '4'
nums.reverse()               # reverse in place
nums.clear()                  # empty the list
MethodEffectReturns
.append(x)Add x to the endNone (modifies in place)
.extend(iterable)Add all items from iterableNone
.insert(i, x)Insert x at index iNone
.remove(x)Remove first occurrence of xNone (raises ValueError if missing)
.pop(i=-1)Remove & return item at index i (default: last)The removed item
.index(x)Find index of first xint (raises ValueError if missing)
.count(x)Count occurrences of xint
.sort()Sort in placeNone
.reverse()Reverse in placeNone
.clear()Remove all itemsNone

+= vs .append() — a Subtle Trap

a = [1, 2, 3]
b = a
b += [4]          # in-place extend — modifies the SAME object a points to
print(a)           # [1, 2, 3, 4] — a changed!
 
c = [1, 2, 3]
d = c
d = d + [4]        # creates a NEW list — does NOT modify c
print(c)            # [1, 2, 3] — unchanged

5. Copying Lists — a Common Trap

Because lists are mutable, b = a does not copy — it aliases. To actually copy:

original = [1, 2, 3]
 
alias = original                 # ✗ NOT a copy — same object
shallow_copy_1 = original.copy() # ✓ copy
shallow_copy_2 = original[:]     # ✓ copy (slicing the whole list)
shallow_copy_3 = list(original)  # ✓ copy
 
shallow_copy_1.append(4)
print(original)          # [1, 2, 3] — unaffected

Shallow copy caveat: if the list contains nested lists, the inner lists are still shared:

import copy
 
nested = [[1, 2], [3, 4]]
shallow = nested.copy()
shallow[0].append(99)
print(nested)        # [[1, 2, 99], [3, 4]] — inner list changed in BOTH!
 
deep = copy.deepcopy(nested)
deep[0].append(100)
print(nested)         # unaffected — deepcopy copies all nested levels too

6. Nested Lists

Lists can contain other lists — commonly used to represent grids, matrices, or rows of data (a preview of what NumPy arrays formalize in Module 6):

matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]
 
print(matrix[1])        # [4, 5, 6]  — second row
print(matrix[1][2])      # 6          — row 1, column 2
 
for row in matrix:
    print(row)

7. Sorting

nums = [5, 2, 8, 1, 9]
 
nums.sort()                        # in place, ascending: [1, 2, 5, 8, 9]
nums.sort(reverse=True)            # in place, descending: [9, 8, 5, 2, 1]
 
original = [5, 2, 8, 1, 9]
new_list = sorted(original)        # returns a NEW sorted list, original unchanged
 
words = ["banana", "kiwi", "apple"]
words.sort(key=len)                 # sort by a custom key function
print(words)                          # ['kiwi', 'apple', 'banana'] — shortest to longest
.sort()sorted()
Modifies original?Yes (in place)No (returns new list)
Works on any iterable?No — list method onlyYes — works on tuples, strings, dicts, etc.
ReturnsNoneNew sorted list

8. Summary & Next Steps

Key Takeaways

  • Lists are ordered and mutable — you can change, add, or remove items after creation.
  • b = a aliases (same object); use .copy(), [:], or list() for a real (shallow) copy, and copy.deepcopy() for nested structures.
  • .sort() mutates in place; sorted() returns a new list — pick based on whether you need the original preserved.
  • Nested lists (lists of lists) are the basis for grid/matrix-style data, revisited with NumPy in Module 6.

Concept Check

  1. Why does b = a; b.append(4) also change a?
  2. What's the difference between .remove(x) and del my_list[i]?
  3. When would you use copy.deepcopy() instead of .copy()?

Next Chapter

Chapter 7: Tuples & Sequence Unpacking


Jr Codex — 1-on-1 Personalized Coaching | Back to Module Index