Week 8: Strings, Dictionaries, and Testing
Submit PA2 to Gradescope
If you don't submit to Gradescope and get a nonzero score, you will receive a reflection grade of no higher than 15/30 points.
🎯 Objectives
In Week 6 your loops walked through lists.
This week they walk through strings, because a string is a sequence too: for ch in "banana": runs six times, once per character.
That one idea opens up a whole kind of program, the kind that reads text and finds something out about it.
Finding something out usually means counting, and counting every letter at once is the job of a dictionary.
A dictionary stores a value under a key, so counts["a"] can hold how many as you have seen, counts["b"] how many bs, and so on, without a variable for each one.
Counting characters in a string is the example this week keeps coming back to, because it is a string loop and a dictionary at the same time.
The third thread is testing.
Since Week 5 you have checked your functions by calling them in the main block and reading the output.
A pytest test writes down the answer you expect, so the checking is done for you, every time, on every case you thought of.
The skill is in choosing the cases.
By the end of the week you should be able to:
- Trace and write a
forloop that steps through the characters of a string, by value or by index, to count, search, and build a new string. - Use the string methods from class that ask about a character or break text apart, like
isalpha(),isspace(), andsplit(). - Construct a dictionary and update it to count or tally values: set a value by key, get it back, add to it, and check whether a key is
inthe dictionary before you look it up. - Say what happens when you look up a key that is not there.
- Write
pytesttests that check a function against ordinary inputs and against edge cases, and run them from the terminal and from VS Code.
Quiz 3 is next week
Quiz 3 is in class next week, covers Weeks 6 and 8, and is done without AI. Your instructor will share the details. Whatever its format, the skill underneath is the same: looking at a loop over a list, a range, or a string, or a dictionary being filled in, and knowing what it does before you run it.
So run your code to check your answers, not to find them.
📚 Study menu
Class covered three things this week: writing and running pytest tests in VS Code, looping through text one character at a time, and counting with a dictionary.
Your PA2 uses all three, so most of the menu points you back at it.
This is a menu, not an assignment. Pick what you need, and with Quiz 3 a week away, make sure at least one item you pick is done by hand, before you run anything.
If you only have an hour
- Fifteen minutes. "Draw the dictionary as it fills," below, on paper.
- Fifteen minutes. "Trace a string loop one character at a time": the
mysterytable only. - Twenty minutes. "Work out the answer before pytest does," for one function of your PA2.
- Ten minutes. Pick the Week 6 activity you skipped, and do just its table.
Read about it
Pick one option. The W3Schools pages are a short skim to pair with either.
Python for Everybody: 7. Strings and 10. Dictionaries
- 7.3 Traversal through a string with a loop, 7.6 Looping and counting, and 7.7 The in operator.
- 10.1 Dictionaries and 10.2 Dictionary as a Set of Counters, which is the dictionary class in one section.
- This book has nothing on testing, so add the TCS chapter below.
How to Think Like a Computer Scientist: 9. Strings, 12. Dictionaries, and 20. Unit Testing
- 9.10 By Item, 9.11 By Index, 9.13 The in and not in operators, 9.14 The Accumulator Pattern with Strings, and 9.16 Looping and Counting.
- 12.1 Dictionaries and 12.2 Dictionary Operations.
- 20.1, 20.2, 20.3, 20.5 Writing Unit Tests, and 20.7 Testing with pytest. This is the only book of the three that covers testing, and 20.3.4, "Unit tests can have bugs," is worth reading twice.
W3Schools: the reference-style skim
- Python Dictionaries, with its Access Items, Change Items, and Add Items subpages.
- String Methods, as a table to look up
isalpha(),isspace(), andsplit()in, not to read top to bottom.
A few things you can skip for now
The books like to run ahead, so here is what to save for later.
You'll see a while loop in TCS 9.12. That's next week's topic, so feel free to skip it.
All three books mix slicing (text[1:4]) into their string chapters, and the W3Schools table lists dozens of string methods. Slicing and most of those methods are coming in Week 10. For now, the handful from class is all you need.
When the books loop over a dictionary (for key in counts:) or use its methods, like get(), they're previewing Week 11. PY4E 10.2 shows get() as a shortcut for counting. It works, but write it the long way, with if key in counts:, because that check is the thing this week is teaching.
And don't worry when TCS 20 starts with plain assert statements instead of pytest. Those assert lines are exactly what goes inside a pytest test, so you're learning the real thing.
Trace a string loop one character at a time
This drill is built like a Quiz 3 question. Copy the two tables onto paper and fill in the right-hand column with a pencil, before you run anything.
def mystery(text: str) -> str:
result = ""
for ch in text:
if ch != " ":
result = ch + result
return result
| Call | Returns |
|---|---|
mystery("abc") |
|
mystery("a b c") |
|
mystery("noon") |
|
mystery("Hi there") |
|
mystery(" ") |
|
mystery("") |
def first_double(text: str) -> int:
for i in range(1, len(text)):
if text[i] == text[i - 1]:
return i
return -1
| Call | Returns |
|---|---|
first_double("letter") |
|
first_double("bookkeeper") |
|
first_double("aa") |
|
first_double("abc") |
|
first_double("a") |
|
first_double("") |
For one row of each table, write a full trace: one line per pass, with the loop variable and result (or i, text[i - 1], and text[i]).
Then check every row in the Shell.
The rules you should be able to state afterward:
- A loop over a string runs once per character, spaces and punctuation included, and zero times over
"". result = ch + resultputs each new character at the front, so it builds the string backward.result = result + chbuilds it forward.- Loop over the characters when you need only each character; loop over
range(len(text))when you need its neighbor or its position. - A
returninside the loop stops the whole function at the first match, which is why-1goes after the loop and not in anelse.
Draw the dictionary as it fills
On paper, trace this loop for word = "hello", writing down the whole dictionary after every pass:
Five passes, five dictionaries.
Then do it again for "Mississippi", where the capital M gets a key of its own.
Now cover the right-hand column, and fill it in for counts = {"h": 1, "e": 1, "l": 2, "o": 1}:
| Expression | Value, or what goes wrong |
|---|---|
counts["l"] |
|
"l" in counts |
|
2 in counts |
|
counts["z"] |
|
"z" in counts |
|
len(counts) |
Last, two broken versions of the loop. Predict each before you run it:
- Delete the
ifand theelse, leaving onlycounts[ch] += 1. - Delete the
ifand theelse, leaving onlycounts[ch] = 1.
What this should leave you with:
- Looking up a key that is not there is a
KeyError, which is why theincheck comes before the lookup. inasks about the keys, not the values.- One of the broken versions crashes on the very first pass; the other runs and quietly gives the wrong answer. A test catches the second kind, and that is the next activity.
Work out the answer before pytest does
A test is only as good as its expected value, and the expected value has to come from you, not from running the function.
If you run your function to find out what to put after ==, the test checks that your function agrees with itself.
Take one function from your PA2 option. On paper, before you write any code for it:
- Write three calls to it, with inputs you made up.
- Work out what each one returns, by hand, following the docstring and the rules on your option's page.
- Turn each into an
assertline in your test file.
Now write the function, and run pytest.
When a test fails, do not change the test to match the function.
Work out the answer by hand once more, and decide which one is wrong.
Sometimes it is the test, and finding that out by hand is exactly the skill Quiz 3 checks.
Hunt for the edge case
Most code is right on ordinary input. It goes wrong at the edges: the smallest case, the empty case, the case where something happens twice. For strings and dictionaries, the edges are usually these:
- the empty string, and a string of one character
- a string with spaces, digits, or punctuation in it
- uppercase where you expected lowercase
- the same character twice in a row, or twice anywhere
- a key that is not in the dictionary yet, or a count that has reached 0
Go through your PA2 functions with this list. For each function, pick the two edges you think are most likely to break it, and write a test for each. Gradescope plants two bugs in a correct PA2 and checks whether your tests catch them; if you used this list, they will.
Then do the same for mystery and first_double above, on paper: which of the rows in those tables are edge cases, and which edge is missing?
Get ready for Quiz 3
Quiz 3 covers Weeks 6 and 8 together, so spend part of your time going back.
- Redo one table from the Week 6 study guide that you skipped or found hard, without looking at your old answers.
- Build the reference card you cannot bring: on one page, from memory, write a loop over a list, over a
range, and over a string; the counting pattern with a dictionary; and a pytest test function. Check it against your notes in a different color, and write it again from memory two days later. - When the Quiz 3 review page appears under Review, try it under quiz conditions: a timer, no notes, and no AI. Then write one line for every wrong answer saying why it was wrong.
Try an AI tutor
AI is a good drill partner for tracing, and a risky one for this week's code.
Ask it to count letters and it will reach for Counter, get(), or a dictionary comprehension.
All of those are real Python, and none of them is what Quiz 3 asks you to read or write, so say so up front.
"I am a CS1 student who knows
forloops over strings and lists,range, and dictionaries withd[key] = value,d[key] += 1, andkey in d. I do not knowwhile, slicing, comprehensions,get(), or looping over a dictionary. Write me an eight-line function that loops over a string and fills in a dictionary, and give me an input. Do not tell me the result. When I give you my answer, tell me only whether it is right.""Here is a function and the docstring for it: [paste it]. Do not write any tests. List five kinds of input that could break it, one line each, and let me write the tests myself."
"Here is a test I wrote: [paste it]. Do not fix it. Tell me only whether each expected value is right, and if one is wrong, which one."
The thing to be skeptical about this week is expected values. An AI asked to write tests will happily write tests that pass, and a test that passes is not the same as a test that is right. Work out every expected value yourself.
✍️ Reflection
Instructions
Download the Week 8 reflection template, type your answers into it, and submit the completed document.
The first three questions are quick; the short answers are the ones that matter. Question 4 is about how you tested your PA2, so answer it after you submit. A few sentences each is plenty. Be specific and be honest.
Submit PA2 to Gradescope
If you don't submit to Gradescope and get a nonzero score, you will receive a reflection grade of no higher than 15/30 points.
1. What did you do this week? (Check all that apply.)
- Read the sections listed for one or more of the textbooks
- Traced the string loops on paper, then checked them in the Shell
- Drew a dictionary after every pass of a counting loop
- Worked out expected values by hand before running pytest
- Went through my PA2 functions looking for edge cases
- Prepared for Quiz 3 (a Week 6 table, a reference card, or the review page)
- Asked an AI tutor to drill me
- Something else (tell us in the short answers)
2. Roughly how much time did you spend on CS 149 outside of class this week?
- Under 1 hour
- 1–2 hours
- 3–4 hours
- 5–6 hours
- 7+ hours
3. Quiz 3 covers loops over lists, ranges, and strings, and dictionaries being filled in. How ready do you feel?
- Ready — I have traced these on paper and checked myself
- Fine with Week 6 loops, less sure about strings or dictionaries
- Fine with strings and dictionaries, less sure about Week 6
- I can write code that works, but I find out what it does by running it
- Not ready, and I know which part is missing
- Not ready, and I am not sure what is missing
4. How did you test your PA2 before you submitted it? Name two of the tests in your test file and say why you chose each one: what could go wrong that it would catch. Did Gradescope report a planted bug that your tests missed, or a test of yours with a wrong expected value? If so, what did you change?
5. Give one thing this week that did not do what you expected. It could be a string loop, a dictionary lookup, or a test that failed. Write what you expected, what actually happened, and the rule you were using that turned out to be wrong. The wrong rule is the useful part. Then say which of this week's objectives feels shakiest and what you plan to do about it before Quiz 3.
6. What is one thing you want gone over in class? Be as specific as you can: "why counts[ch] += 1 crashes on a new letter" is useful, and "dictionaries" is not. Answer this even if you feel confident, by naming the kind of question you would least like to see on Quiz 3. Class time gets spent on whatever shows up most often in these answers.
7. Did you use an AI tool this week, and did it build your understanding or replace it? The honest test for this week: given a short function that loops over a string and fills in a dictionary, could you write the dictionary after every pass on paper, and write three tests for it with expected values you worked out yourself? If a video, article, or tutorial made something click, drop the link and one line on why it helped; we collect these to improve the study menu for future students.
8. Paste your PA2 code, both the module and your test file. If tracing one of your functions on paper showed you something that running it did not, say what.