11  Lists

11.1 The Wall

AI used append() in one place and extend() in another. The output had lists nested inside lists when it should have been flat. You asked AI to fix it and it replaced everything with list comprehensions you could not read. You did not know enough about lists to tell AI which operation was wrong.

Lists are Python’s workhorse data structure. AI uses them in nearly every non-trivial program. If you cannot tell append from extend, or read a list comprehension, you are stuck accepting whatever AI produces.

This chapter fixes that.

NoteComing from Think Python?

You may recognise lists from projects like the Quiz Game or Contact Book in Think Python, Direct AI. There you used lists to store questions or entries. This chapter explores lists more deeply, indexing, slicing, methods, and the patterns that make lists essential.

11.2 Thinking Session

11.2.1 Getting Oriented

NoteThinking Session Prompt

Explain Python lists to me. Not just “an ordered collection”. I want to understand: how do I access individual items, what happens when I try to access an item that does not exist, and what is the difference between a list and a string? They seem similar.

Your AI should cover indexing (starts at 0), negative indexing (-1 is last item), IndexError for out-of-range access, and the key difference: lists are mutable (you can change items), strings are not.

11.2.2 Go Deeper

NoteThinking Session Prompt

What are the most important list methods? I keep seeing append(), extend(), insert(), remove(), pop(), sort(), and reverse() in AI-generated code. Explain each one and, critically, tell me which ones modify the list in place versus returning a new list.

TipWhat to Look For

The key distinction: append(), extend(), insert(), remove(), pop(), sort(), reverse() all modify the list in place and return None. sorted() returns a new list. AI sometimes writes my_list = my_list.sort(). This sets my_list to None. If your AI does not mention this trap, ask about it.

NoteThinking Session Prompt

What is list slicing in Python? I see notation like items[1:3], items[:5], items[::2] in AI-generated code. Explain the slice notation and give me practical examples.

11.2.3 Challenge It

NoteThinking Session Prompt

What is wrong with each of these, and what should they be?

items = [1, 2, 3]
items = items.append(4)

data = [3, 1, 2]
data = data.sort()

combined = [1, 2] + 3
TipWhat to Look For

Line 2: append() returns None, so items becomes None. Fix: just items.append(4). Line 5: same issue. sort() returns None. Fix: data.sort() alone, or data = sorted(data). Line 7: cannot add an integer to a list. Fix: [1, 2] + [3] or [1, 2].append(3).

11.2.4 What You Should Have Learned

  • Lists are ordered, mutable sequences: [1, 2, 3]
  • Indexing starts at 0, negative indexes count from the end
  • append() adds one item, extend() adds multiple (both in place)
  • sort() modifies in place and returns None; sorted() returns a new list
  • Slicing: items[start:stop:step]
  • List comprehensions: [x * 2 for x in items]

11.3 The Gap

You can now read AI-generated list operations and tell whether they are correct. When AI writes result = items.sort(), you know the bug. When it uses append where extend belongs, you can fix it. When it writes a list comprehension, you can parse what it does.

In the Building Session, you will add conversation history to your chatbot using lists.

11.4 Building Session

11.4.1 The Spec

Add list-based features to your chatbot:

  • Store conversation history as a list of messages
  • Pick random responses from a list of options
  • Add a “history” command that shows recent messages
  • Use list slicing to show only the last 5 messages

11.4.2 Prompt It

NoteBuilding Session Prompt

Update my chatbot to v1.1. Add conversation history using lists:

  • Create a conversation_history list that stores each exchange as a string: “User: message” and “Bot: response”
  • Add a “history” command that shows the last 5 entries using list slicing
  • For the default response, use random.choice() to pick from a list of 4-5 varied responses instead of always saying the same thing
  • Add import random at the top

Keep the categorised response system from v1.0.

11.4.3 Read the Code

Your AI will produce something like this:

import random

def get_response(user_input):
    """Categorise input and return response."""
    text = user_input.lower().strip()

    if text in ("bye", "goodbye", "quit"):
        return "Goodbye!", True
    elif text.startswith(("hello", "hi", "hey")):
        return random.choice([
            "Hello! Great to hear from you.",
            "Hi there! What's on your mind?",
            "Hey! How can I help?",
        ]), False
    elif "?" in text:
        return "Good question! Let me think...", False
    else:
        return random.choice([
            f"Interesting. '{user_input}'.",
            "Tell me more about that.",
            "I see. Go on...",
            "That's a new one for me!",
        ]), False
TipWhat to Notice

random.choice() picks one item from a list. This gives varied responses. The conversation history uses append() to add entries (in the main loop, not shown here). history[-5:] slices the last 5 items. Lists grow dynamically. No need to declare a size.

11.4.4 Stretch It

NoteBuilding Session Prompt

Add a “search” command that takes a word and searches the conversation history for any messages containing that word. Use a list comprehension to filter matches.

11.5 Your Chatbot So Far

  • Ch 1-9: Loop, types, memory, formatting, validation, operators, functions
  • Ch 10: Categorised responses with if/elif/else
  • Ch 11: Conversation history list, random responses, history command

11.6 Quick Reference

# Create
items = [1, 2, 3]
items = list(range(5))       # [0, 1, 2, 3, 4]

# Access
items[0]                     # first item
items[-1]                    # last item
items[1:3]                   # slice: [2, 3]
items[-3:]                   # last 3 items

# Modify (in place, return None)
items.append(4)              # add one item
items.extend([5, 6])         # add multiple
items.insert(0, "first")     # insert at index
items.remove(3)              # remove by value
items.pop()                  # remove and return last
items.sort()                 # sort in place

# New list
sorted(items)                # returns sorted copy
[x * 2 for x in items]      # list comprehension

# Check
3 in items                   # True/False
len(items)                   # count