Skip to content
aviral gupta

// B4.1 · ~25 min · Beginner

List methods, stacks and queues

After this lesson you can change a list with its methods, choose between sort() and sorted(), and use a list as a stack and a deque as a queue.

Lesson 1 of 6 in B4 Data structures

Start of the module

You will be able to

  • Change a list with append, extend, insert, remove and pop, and search it with index and count
  • Choose between list.sort(), which sorts in place, and sorted(), which returns a new list
  • Use a list as a stack and collections.deque as a queue
  1. Warm-up · Activity 1 of 7

    Warm-up from module B1: nums = [4, 8, 15]. Which expressions give 15? Pick all that apply.

    Select all that apply.

  2. Predict · Activity 2 of 7

    Predict before you read on: what does this print?

    a = [1, 2]
    a.append([3, 4])
    b = [1, 2]
    b.extend([3, 4])
    print(len(a), len(b))
  3. Practice · Activity 3 of 7

    Fill in the method that removes the last item and gives it back, so top is 3 and stack is [1, 2].

    stack = [1, 2, 3]
    top = stack.____()
    top = stack.()
  4. Practice · Activity 4 of 7

    What does this print?

    nums = [3, 1, 2]
    result = nums.sort()
    print(result, nums)
  5. Practice · Activity 5 of 7

    Each call starts from its own nums = [5, 2, 7, 2]. Match the call to what it does.

    nums = [5, 2, 7, 2]
  6. Brain teaser · Activity 6 of 7

    Brain teaser. A deque can add and remove at both ends. What does this print?

    from collections import deque
    
    line = deque(["ana", "ben"])
    line.append("cleo")
    line.appendleft("dan")
    print(line.popleft(), line.pop())
  7. Apply · Activity 7 of 7

    Mini-task. Write balanced(text), which returns True when every bracket ( [ { in text is closed by the matching ) ] } in the right order. Use a list as a stack: push each opening bracket, and pop when a closing one arrives. Try "(a[b]{c})", "(]", "(()" and "x)".

    Check your work against this list

Build it yourself

Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.

Worked example

A print queue and an undo stack

An editor keeps two collections. Print jobs wait in a deque and are printed in the order they arrived. Edits go on a list, and undo takes the newest one back first. At the end the program sorts a list of page counts twice: once into a new list with sorted(), once in place with sort(). Run it, then add a job or an edit and run it again.

main.py

from collections import deque

# Print jobs are served in the order they arrive: a queue.
jobs: deque[str] = deque()
jobs.append("report.pdf")
jobs.append("photo.png")
jobs.append("invoice.pdf")

# Edits are undone newest first: a stack.
edits: list[str] = []
edits.append("type 'Hello'")
edits.append("make bold")
edits.append("delete line 3")

print("Printing:", jobs.popleft())
print("Next up:", jobs[0])
print("Undo:", edits.pop())
print("Undo:", edits.pop())
print("Edits left:", edits)

pages = [12, 3, 7]
print("Sorted copy:", sorted(pages), "original:", pages)
pages.sort(reverse=True)
print("Sorted in place:", pages)

Run it with

python main.py

Output

Printing: report.pdf
Next up: photo.png
Undo: delete line 3
Undo: make bold
Edits left: ["type 'Hello'"]
Sorted copy: [3, 7, 12] original: [12, 3, 7]
Sorted in place: [12, 7, 3]
  • popleft() takes the oldest job, and jobs[0] shows the next one without removing it.
  • pop() takes the newest edit, so the edits come back in reverse order.
  • sorted(pages) left pages as it was; pages.sort(reverse=True) changed it.
Change it and run it

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.

Exercises

Exercise 1 of 2

Top three without side effects

top_three(scores) should return the three highest scores, highest first. The starter gets the right answer, but it also reorders the list the caller passed in. Fix it so the caller’s list stays exactly as it was.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.

Hints
  1. Hint 1

    scores.sort() changes the caller’s list, because the function gets the same list object, not a copy.

  2. Hint 2

    sorted() returns a new list and leaves its argument alone.

  3. Hint 3

    return sorted(scores, reverse=True)[:3]

Show a solution

One way to solve it. Yours can look different and still pass the checks.

def top_three(scores: list[int]) -> list[int]:
    """Return the three highest scores, highest first."""
    return sorted(scores, reverse=True)[:3]
Run it on your computer

Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.py

def top_three(scores: list[int]) -> list[int]:
    """Return the three highest scores, highest first."""
    scores.sort(reverse=True)
    return scores[:3]

test_main.py

from main import top_three


def test_highest_first():
    """Returns the three highest scores, highest first"""
    got = top_three([40, 90, 10, 70, 55])
    assert got == [90, 70, 55], f"top_three([40, 90, 10, 70, 55]) returned {got!r}, expected [90, 70, 55]"


def test_list_unchanged():
    """Leaves the caller's list in its original order"""
    scores = [40, 90, 10, 70, 55]
    top_three(scores)
    assert scores == [40, 90, 10, 70, 55], f"after the call the caller's list is {scores!r}; it must not change"


def test_short_list():
    """Works with fewer than three scores"""
    got = top_three([5, 8])
    assert got == [8, 5], f"top_three([5, 8]) returned {got!r}, expected [8, 5]"

On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.

Run the program:

python main.py

Run the checks (needs learnrun.py in the same folder):

python learnrun.py test
Download learnrun.py

Exercise 2 of 2

A fair queue

serve_all(commands) runs a list of commands. "join ana" puts ana in the queue; "serve" serves whoever has waited longest and records the name, or records "nobody" when the queue is empty. Return the recorded names in order. The starter serves the newest person instead and crashes on an empty queue. Use collections.deque.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.

Hints
  1. Hint 1

    waiting.pop() takes the newest name. A queue must take the oldest, from the front.

  2. Hint 2

    from collections import deque, then waiting = deque() and waiting.popleft().

  3. Hint 3

    Check if waiting: before popleft(); an empty deque is false.

Show a solution

One way to solve it. Yours can look different and still pass the checks.

from collections import deque


def serve_all(commands: list[str]) -> list[str]:
    """Run join and serve commands; return who was served, in order."""
    waiting: deque[str] = deque()
    served = []
    for command in commands:
        if command.startswith("join "):
            waiting.append(command[5:])
        elif command == "serve":
            if waiting:
                served.append(waiting.popleft())
            else:
                served.append("nobody")
    return served
Run it on your computer

Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.py

def serve_all(commands: list[str]) -> list[str]:
    """Run join and serve commands; return who was served, in order."""
    waiting = []
    served = []
    for command in commands:
        if command.startswith("join "):
            waiting.append(command[5:])
        elif command == "serve":
            # Serve the person who has waited longest,
            # or record "nobody" when no one is waiting.
            served.append(waiting.pop())
    return served

test_main.py

from main import serve_all


def test_first_come_first_served():
    """Serves people in the order they joined"""
    got = serve_all(["join ana", "join ben", "serve", "join cleo", "serve", "serve"])
    assert got == ["ana", "ben", "cleo"], f"served {got!r}, expected ['ana', 'ben', 'cleo']"


def test_nobody_waiting():
    """Records nobody when the queue is empty"""
    got = serve_all(["serve", "join ana", "serve", "serve"])
    assert got == ["nobody", "ana", "nobody"], f"served {got!r}, expected ['nobody', 'ana', 'nobody']"


def test_no_commands():
    """Serves no one when there are no commands"""
    got = serve_all([])
    assert got == [], f"serve_all([]) returned {got!r}, expected []"

On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.

Run the program:

python main.py

Run the checks (needs learnrun.py in the same folder):

python learnrun.py test
Download learnrun.py

Common mistakes

Keeping the result of sort()

scores = [30, 10, 20]
scores = scores.sort()
print(scores[0])

What Python prints

TypeError: 'NoneType' object is not subscriptable

Why, and the fix

sort() sorts the list in place and returns None, so scores = scores.sort() throws the list away and keeps None. Call scores.sort() on its own line, or write scores = sorted(scores) when you want a new list.

Removing a value that is not there

guests = ["ana", "ben"]
guests.remove("zoe")

What Python prints

ValueError: list.remove(x): x not in list

Why, and the fix

remove raises ValueError when no item equals the value. Check first with if "zoe" in guests: guests.remove("zoe"). index behaves the same way for a missing value.

Popping from an empty stack

undo = []
last = undo.pop()

What Python prints

IndexError: pop from empty list

Why, and the fix

An empty list has nothing to pop. Guard the call with if undo: … because an empty list is false. A deque raises IndexError too, with the message "pop from an empty deque".

Python in the browser: Pyodide 314.0.7, MPL-2.0. Licence and source

Exit ticket

5 questions, no hints. Score 80% or more to complete the lesson.

Finish every activity above to unlock the exit ticket.

Report a problem

Spotted something wrong or unclear? Say what, and it will be checked and fixed.

#

At least 20 characters.

Only if you want a reply.

Key ideas

Methods that change the list

append(x) adds one item at the end; extend(items) adds each item of another sequence. insert(i, x) puts x before position i. remove(x) deletes the first item equal to x and raises ValueError if there is none. pop(i) deletes the item at position i and returns it; without i it takes the last item. index(x) gives the position of the first x, and count(x) how many there are. Methods that only change the list, such as append, insert, remove and sort, return None.

sort() or sorted()

nums.sort() sorts the list itself and returns None, so result = nums.sort() leaves result as None. sorted(nums) leaves nums alone and returns a new sorted list; it accepts any sequence, even a string. Both take reverse=True for descending order and key= for a function that gives each item its sort value, such as key=len. Items must be comparable: sorting a list that mixes numbers and strings raises TypeError.

Stacks and queues

A stack gives back the newest item first ("last in, first out"), like an undo history. A list does this well: append() to push, pop() to take from the end. A queue gives back the oldest item first ("first in, first out"), like people at a counter. list.pop(0) works but is slow on long lists, because every other item has to move. collections.deque is built for this: append() at the back, popleft() at the front, both fast.

Sources

Last reviewed September 29, 2026