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.
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
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
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.
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.
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
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] + 3Line 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
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!",
]), Falserandom.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
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