⚡ Phase 2 · Control Flow 🟡 Beginner+ MODULE 08

Lists & Tuples

⏱️ 40 min read
📖 Theory + Code
🧩 5 Quiz Questions
🏗️ 1 Challenge
Phase 2 progress50%
🎯 What you'll learn: How to store ordered collections of data with Python lists — creation, indexing, slicing, all essential methods, sorting, list comprehensions, and nested lists. Then the sister type tuple — what makes it different, when to use it, and how packing/unpacking works.

Lists vs Tuples at a Glance

Both lists and tuples store ordered sequences of items and support indexing, slicing, and for loops. The key difference is mutability: lists can be changed after creation; tuples cannot.

📋
list
[ ]
  • Mutable — items can be added, removed, changed
  • Ordered — items keep their insertion order
  • Allows duplicate values
  • Use for collections that change: shopping cart, student marks, to-do items
  • Has many built-in methods (.append, .sort, .pop…)
🔒
tuple
( )
  • Immutable — cannot be changed after creation
  • Ordered — items keep their insertion order
  • Allows duplicate values
  • Use for fixed data: coordinates, RGB colours, database rows
  • Slightly faster and uses less memory than list

Creating & Accessing Lists

A list is created with square brackets [ ]. Items can be of any type — even mixed types or other lists. Access items with an index (0-based), and use negative indexes to count from the end — exactly like strings.

lists_basics.py
PYTHON
# Creating lists
fruits  = ["apple", "banana", "mango", "grape"]
marks   = [85, 92, 78, 95, 88]
mixed   = ["Ali", 21, True, 3.14]   # any types allowed
empty   = []                         # empty list
nested  = [[1,2], [3,4], [5,6]]   # list of lists

# Indexing (0-based)
print(fruits[0])    # apple   (first)
print(fruits[2])    # mango
print(fruits[-1])   # grape   (last)
print(fruits[-2])   # mango   (second from last)

# Slicing — same as strings
print(fruits[1:3])  # ['banana', 'mango']
print(fruits[:2])   # ['apple', 'banana']
print(fruits[::-1]) # reversed list

# Modify an item (lists are mutable!)
fruits[1] = "blueberry"
print(fruits)       # ['apple', 'blueberry', 'mango', 'grape']

# Length and membership
print(len(fruits))           # 4
print("mango" in fruits)   # True

# Nested list access
print(nested[1][0])          # 3 (second list, first element)

Essential List Methods

Lists come with powerful built-in methods. These are called with dot notation: list.method(). Unlike string methods, most list methods modify the list in-place and return None — they don't create a new list.

MethodDescriptionReturnsExample
.append(x)Add x to the endNonelst.append(5)
.insert(i, x)Insert x at index iNonelst.insert(0, "first")
.extend(iter)Add all items from iterableNonelst.extend([4,5,6])
.remove(x)Remove first occurrence of xNonelst.remove("apple")
.pop(i)Remove & return item at index i (default: last)itemlast = lst.pop()
.sort()Sort in-place (ascending by default)Nonelst.sort(reverse=True)
.reverse()Reverse the list in-placeNonelst.reverse()
.index(x)Return index of first occurrence of xintlst.index("mango")
.count(x)Count occurrences of xintlst.count(5)
.copy()Return a shallow copylistb = lst.copy()
.clear()Remove all itemsNonelst.clear()
list_methods.py
PYTHON
students = ["Ali", "Sara", "Zara"]

students.append("Omar")          # add to end
students.insert(1, "Bilal")      # insert at index 1
print(students)  # ['Ali', 'Bilal', 'Sara', 'Zara', 'Omar']

students.remove("Zara")          # remove by value
last = students.pop()             # remove last, return it
print(f"Removed: {last}")          # Omar

students.sort()                    # alphabetical sort
print(students)  # ['Ali', 'Bilal', 'Sara']

# sorted() — returns NEW sorted list, original unchanged
marks   = [72, 95, 61, 88]
ranked  = sorted(marks, reverse=True)
print(ranked)    # [95, 88, 72, 61]
print(marks)     # [72, 95, 61, 88] — unchanged!

# Built-in functions on lists
print(sum(marks))     # 316
print(max(marks))     # 95
print(min(marks))     # 61
print(len(marks))     # 4
⚠️
.sort() modifies in-place — sorted() returns a new list
marks.sort() permanently reorders the original list and returns None. sorted(marks) leaves the original unchanged and returns a brand-new sorted list. Use sorted() when you need to keep the original order — which is almost always the safer choice.

List Comprehensions

A list comprehension is Python's most elegant feature for creating lists. It collapses a loop-and-append pattern into a single, readable line. Once you learn it, you'll use it everywhere.

[ expression for item in iterable ]
[ expression for item in iterable if condition ]
list_comprehensions.py
PYTHON
# Old way: loop + append
squares = []
for i in range(1, 6):
    squares.append(i ** 2)

# Comprehension — same result, one line
squares = [i ** 2 for i in range(1, 6)]
print(squares)     # [1, 4, 9, 16, 25]

# With a filter condition
evens  = [x for x in range(1, 11) if x % 2 == 0]
print(evens)       # [2, 4, 6, 8, 10]

# Transform strings
names  = ["  ali  ", "  SARA ", "zara"]
clean  = [n.strip().title() for n in names]
print(clean)       # ['Ali', 'Sara', 'Zara']

# Filter + transform: passing marks only
marks  = [55, 42, 88, 31, 76, 90]
passed = [m for m in marks if m >= 50]
print(passed)      # [55, 88, 76, 90]

# Ternary inside comprehension
grades = ["Pass" if m >= 50 else "Fail" for m in marks]
print(grades)      # ['Pass', 'Fail', 'Pass', 'Fail', 'Pass', 'Pass']

# Flatten a nested list
matrix = [[1,2],[3,4],[5,6]]
flat   = [n for row in matrix for n in row]
print(flat)        # [1, 2, 3, 4, 5, 6]
Comprehensions are faster than for + append
Python internally optimises list comprehensions — they run about 30–40% faster than an equivalent for-loop with append. For large datasets, this matters. But more importantly, they're more readable once you know the syntax — a single glance tells you what the output list contains.

Tuples — Immutable Sequences

Tuples are created with parentheses ( ) or just commas. They support indexing, slicing, and for loops exactly like lists — but you cannot add, remove, or change their items after creation.

tuples.py
PYTHON
# Creating tuples
point   = (3, 7)             # 2D coordinate
rgb     = (255, 128, 0)     # orange colour
person  = ("Ali", 22, "Lahore")
single  = (42,)              # MUST have trailing comma for single item!
packed  = 1, 2, 3           # parentheses optional (packing)

# Indexing and slicing — same as list
print(rgb[0])      # 255
print(rgb[-1])     # 0
print(rgb[:2])     # (255, 128)

# Attempting to modify raises TypeError
# rgb[0] = 200   ← TypeError: 'tuple' object does not support item assignment

# Tuple unpacking — assign all values in one line
name, age, city = person
print(name, age, city)   # Ali 22 Lahore

# Swap variables using tuple packing/unpacking
a, b = 10, 20
a, b = b, a
print(a, b)             # 20 10

# Extended unpacking with *
first, *rest = (1, 2, 3, 4, 5)
print(first)   # 1
print(rest)    # [2, 3, 4, 5]

*head, last = (1, 2, 3, 4, 5)
print(last)    # 5
🎭
Functions return tuples — you unpack them without realising it
When Python code returns multiple values like return name, age, it's really returning a single tuple (name, age). When you write n, a = get_info() you're tuple-unpacking. This is why tuple unpacking is one of the most important Python patterns to master.

Practical Patterns

📋
List as a stack (LIFO)
Use .append(x) to push, .pop() to pull from the top. Stacks power undo systems, call stacks, and expression parsers.
📬
List as a queue (FIFO)
Use .append(x) to enqueue, .pop(0) to dequeue from the front. For performance, use collections.deque instead.
🗺️
zip() with lists
list(zip([1,2,3], ["a","b","c"]))[(1,"a"), (2,"b"), (3,"c")]. Combine two parallel lists into pairs.
⚠️
Copy trap — shallow vs deep
b = a makes b point to the SAME list. Use b = a.copy() or b = a[:] for an independent copy. Otherwise changing b also changes a.
practical_lists.py
PYTHON
# ── Stack (undo history) ─────────────────────
history = []
history.append("typed 'hello'")
history.append("deleted word")
history.append("pasted image")
last_action = history.pop()
print(f"Undone: {last_action}")   # Undone: pasted image

# ── Copy trap ────────────────────────────────
a = [1, 2, 3]
b = a          # b IS a — same object!
b.append(4)
print(a)       # [1, 2, 3, 4]  ← a was changed too!

a = [1, 2, 3]
b = a.copy()   # independent copy
b.append(4)
print(a)       # [1, 2, 3]    ← a is safe

# ── Tuple as function "return multiple values" ─
def min_max(nums):
    return min(nums), max(nums)   # returns a tuple

lo, hi = min_max([8, 3, 15, 1, 9])
print(lo, hi)  # 1 15

# ── Convert between list and tuple ───────────
t = (1, 2, 3)
l = list(t)    # tuple → list (now mutable)
l.append(4)
t = tuple(l)  # list → tuple (now immutable again)
print(t)      # (1, 2, 3, 4)
🧩 Knowledge Check
5 questions — Lists & Tuples
1. What is the key difference between a list and a tuple?
2. What does [x**2 for x in range(4)] produce?
3. After b = a where a = [1,2,3], if you do b.append(4), what is a?
4. What does .pop() do with no argument?
5. How do you create a tuple with a single item 42?
🏗️
Coding Challenge — Student Marks Manager
Lists, comprehensions, sorting, tuples, and unpacking combined
Task: Build a student marks manager that:

1. Asks the user to enter 5 student names and their marks (use two lists built with a while loop)
2. Uses zip() to pair names and marks, then stores as a list of tuples
3. Uses a list comprehension to extract only passing students (mark ≥ 50)
4. Sorts the full list by mark descending using sorted() with a key argument
5. Unpacks each tuple to print a ranked leaderboard with position, name, mark, and grade
6. Calculates and prints the class average, highest, and lowest mark
💡 Show hints
  • Store as tuples: records = list(zip(names, marks))
  • Sort by mark: sorted(records, key=lambda x: x[1], reverse=True)
  • Unpack in loop: for pos, (name, mark) in enumerate(ranked, 1):
  • Grade ternary: "A" if mark >= 80 else "B" if mark >= 65 else "C" if mark >= 50 else "F"
marks_manager.py — Sample Solution
PYTHON
# ── Student Marks Manager ─────────────────────
names = []; marks = []
print("Enter 5 students (name then mark):\n")
while len(names) < 5:
    n = input(f"  Student {len(names)+1} name : ").strip().title()
    m = int(input(f"  {n}'s mark (0-100): "))
    names.append(n); marks.append(m)

records = list(zip(names, marks))
passing = [r for r in records if r[1] >= 50]
ranked  = sorted(records, key=lambda x: x[1], reverse=True)

def grade(m):
    return "A" if m>=80 else "B" if m>=65 else "C" if m>=50 else "F"

print(f"\n{'═'*40}")
print("  🏆  LEADERBOARD")
print(f"{'─'*40}")
for pos, (name, mark) in enumerate(ranked, 1):
    print(f"  {pos}. {name:12} {mark:3}  Grade: {grade(mark)}")
print(f"{'─'*40}")
print(f"  Average : {sum(marks)/len(marks):.1f}")
print(f"  Highest : {max(marks)}")
print(f"  Lowest  : {min(marks)}")
print(f"  Passing : {len(passing)}/{len(records)}")
print(f"{'═'*40}")
🎉
Lesson 8 Complete!
Lists and tuples mastered — the foundation of all Python data handling. Next: Dictionaries & Sets!
← Course Home
Phase 2 · Control FlowLesson 8 of 6