This lab has three sections, and they are used at three different points in the class.
Section
When
Submitted?
Diagnostic
First, on your own
No
Part A—Discussion
The reasoning block, in pairs, away from the keyboard
No
Part B—Build
Lab parts 1 and 2, at the keyboard
Graded on what you do, not what you upload
Conditions. No AI assistants. You may consult the official Python documentation and these course notes freely—function names are meant to be looked up, reasoning is not.
Every problem carries a timer and a Focus button. Start the clock when you start the problem; press Focus to blank out everything else on the page. The suggested times are budgeted against the real session rather than invented, so each section’s times sum to roughly the time that section actually gets.
This week only, nothing here is graded. Aug 25 is a free week: the lab, the diagnostic, and participation all count for zero. Grading begins Sep 1. Use the week to find out where you stand while it is still cheap.
Diagnostic
Six short items, twenty minutes in total. They are not a test of cleverness and there is no trick in any of them; every one is a thing this course assumes you can already do by the second week. Work them under lab conditions—no AI assistants, no notes.
This is not graded and no one sees your result but you. Its only purpose is to tell you honestly whether your Python is where the course assumes it is, while you still have time to act. Homework will not tell you: AI assistants are permitted there, so a gap can stay hidden until the midterm. Labs and exams are unaided and are 45% of your grade between them.
Run the self-check cell under each item. It will tell you whether your answer is right without telling you what the answer is.
Note D1. Default and keyword arguments
Write report(values, digits=2, label="mean"). It returns the string "<label>: <rounded mean>"—for example report([1, 2, 4]) returns "mean: 2.33". Both digits and label must work when passed by keyword, in either order.
Note D2. Comprehensions
From the list words below, build two things with comprehensions:
lengths, a list of the length of every word;
long_words, a dict mapping each word of length 4 or more to its length.
Note D3. Nested indexing
Using only indexing—no loops—pull three values out of roster:
ada_last_hw: Ada’s most recent homework score;
linus_first_lab: Linus’s first lab score;
grace_hw_total: the sum of Grace’s homework scores.
Note D4. Loops and branching
Write tally(numbers), which returns a dict with keys "negative", "zero" and "positive" counting how many entries fall in each class. Use a loop and if/elif/else. All three keys must be present even when their count is 0.
Note D5. Read a traceback
This code crashed:
Traceback (most recent call last):
File "cart.py", line 9, in <module>
print(total_price(cart))
~~~~~~~~~~~^^^^^^
File "cart.py", line 5, in total_price
total += item["price"] * item["qty"]
~~~~^^^^^^^
KeyError: 'qty'
Answer two questions out loud before you touch the code: which exception is it, and which line actually raised it—the one named first in the traceback, or the one named last?
Then fix total_price so that an item with no "qty" counts as a quantity of one. Do not edit cart.
Note D6. Install and import
On your own laptop, in a terminal, create and activate the course virtual environment and install the course packages:
Then, in the notebook you are submitting, import NumPy and print its version. Write the version you got on your laptop as a comment—the browser cell below runs its own copy and will not match.
WarningReading your result honestly
Zero or one item gave you trouble. You are where the course assumes you are. Carry on.
Two or three. Work through the catch-up resources this week, not next month.
Four or more. Come to office hours in the first two weeks. Students may drop through the end of the fourth week without a W, and I would much rather have that conversation in week two than in week six. The material gets harder from here and it never returns to this level.
The failure mode this diagnostic exists to prevent is the student who gets through six weeks of AI-assisted homework, arrives at the midterm, and discovers there that they cannot write a for loop unaided.
Part A—Discussion
Not submitted. This is where the participation grade is earned.
Work in pairs, away from the keyboard. One of you plays the interviewer and holds the rubric card; the other answers out loud, in sentences, without typing. Then swap for the next question.
Take the interviewer’s seat seriously. Knowing what a good answer contains is a harder skill than producing one, and the person holding the rubric usually learns more than the person answering. Your job is to listen for the bullets, notice which one is missing, and push the follow-up probe when the answer is thin. “Correct but incomplete” is the most common outcome and the most useful thing you can name out loud.
Each question is labelled by the role and stage at which a question of that shape gets asked. That framing is deliberate: no interview lets you use an AI assistant. This lab is not simulating a workplace, it is simulating the gate you have to pass to reach one—a better answer to “why can’t I use AI in labs?” than any policy paragraph I could write. The five-minute clock on each question is part of the simulation too. Real answers are time-boxed, and an answer that needs twelve minutes to arrive is, in that room, a wrong answer.
The five questions are budgeted at five minutes each, which is the twenty-five minutes the reasoning block gets.
Note A1. Two correct solutions
Phone screen, data science generalist.
“Both of these return the second largest element and both are correct. Which one would you put in production, and why?”
def second_a(a):returnsorted(set(a))[-2]def second_b(a): best = second =float("-inf")for x in a:if x > best: best, second = x, bestelif best > x > second: second = xreturn second
TipRubric card—interviewer only
A good answer contains:
The cost difference named properly: sorting is O(n\log n), the single pass is O(n), and both touch the list once otherwise.
A reason the slower one might still win: it is one line, obviously correct, and a reader can verify it at a glance.
A statement of when the difference matters—n in the millions, or a call inside a loop—and an admission that for n=6 it does not.
Some notice that the two disagree on inputs like [5, 5] or [], so “both are correct” was quietly doing work.
Common wrong answer to listen for: “second_b is better because it is faster.” Faster is not a property of code in isolation; it is a property of code at a given input size. Push back.
Follow-up probe: “Your team runs this on lists of length six, ten thousand times a second. Same answer?”
Note A2. is versus ==
Phone screen, any Python role.
“What is the difference between is and ==? Give me a case where using the wrong one silently gives you the wrong answer.”
TipRubric card—interviewer only
A good answer contains:
== asks about value, is asks about identity—the same object in memory.
A concrete failure: a = [1, 2]; b = [1, 2], then a == b is true and a is b is false.
The place where is is correct: x is None, and comparison against singletons generally.
Ideally, an admission that small integers and short strings sometimes make is appear to work, which is exactly what lets the bug survive testing.
Common wrong answer to listen for: “is is just a faster ==.” It does not compare contents at all, and a candidate who believes this will write if name is "Ada", pass their own tests, and fail in production.
Follow-up probe: “You tried it in the REPL with x = 5 and is worked fine. Why should I not trust that experiment?”
The default is evaluated once, when the def statement runs—not once per call.
Therefore every call that omits log shares one list, and results accumulate: [1], then [1, 2], then [1, 2, 3].
The standard fix: log=None, then if log is None: log = [] inside. Note that this is is, not ==, which ties straight back to A2.
The general principle: mutable objects as defaults are a trap; immutable ones (0, None, "x", tuples) are safe.
Common wrong answer to listen for: “Each call gets a fresh empty list.” That is what the code looks like it says, and believing it is the entire bug.
Follow-up probe: “Is def f(x, n=0) dangerous in the same way? Why not?”
Note A4. When a comprehension stops helping
Onsite, analytics role at a mid-size company.
“When would you rewrite a comprehension as a plain for loop? Use this one to show me where the line is.”
out = [f(x) for row in grid for x in row if x isnotNoneand g(x) >0]
TipRubric card—interviewer only
A good answer contains:
What a comprehension buys: it announces up front that the result is a new collection, one item per input, with no side effects.
What destroys that: more than one for, a condition that no longer fits on a line, side effects, or anything needing a try.
The nesting-order confusion in the example—the for clauses read left to right like nested loops, which almost nobody gets right on first reading.
Readability argued for a reader, not as personal taste: could a classmate say what this line returns in five seconds?
Common wrong answer to listen for: “Comprehensions are faster, so always use them.” The speed difference is small and irrelevant beside the cost of a line nobody can read.
Follow-up probe: “Does your answer change if this line lives in a script you throw away tomorrow, versus a module five people import?”
Note A5. What breaks it
Onsite, data science generalist. Asked the moment a candidate stops typing.
“You have written second_largest. Before I run it—what inputs break it, and what should it do on each?”
TipRubric card—interviewer only
A good answer contains:
At least three genuine edge cases: the empty list, a one-element list, and a list whose maximum repeats such as [5, 5, 4].
A decision, stated aloud, about what “second largest” means when the maximum repeats—the second distinct value, or the second slot—and the observation that the specification, not the code, has to settle it.
What the function should do instead of crashing: return None, or raise a deliberate ValueError carrying a message.
Ideally: negative inputs, and the fact that float("-inf") as a sentinel quietly assumes the input is numeric.
Common wrong answer to listen for: “It works on all valid inputs.” Ask who decides what valid means, and what the function does when it gets something else.
Follow-up probe: “You return None for the empty list. The caller does arithmetic on your result. Have you helped them or hurt them?”
Part B—Build
ImportantRoles in this lab
Work in a group of three for Part B and rotate the jobs from last week:
Driver types. Nobody else touches the keyboard.
Navigator reads the documentation and decides where to go next, and never types.
Skeptic owns the self-check cells and answers one question about every result: what would tell us this is wrong?
At the checkpoint I will pose a question, give you a minute to confer, and call on someone at random to answer for the group. Confer until everyone at your table could answer it.
Ungraded this week only.
Fill in each cell, then run the self-check below it. A self-check tells you whether your answer meets the specification; it does not tell you the answer. Read each specification carefully—the edge cases in it are the point, and they are exactly what A5 was about.
The suggested times sum to roughly the seventy minutes of keyboard time the session has: twenty-five before the break, forty-five after.
Lab, part 1
Note B1. Second largest
Return the second largest distinct value in a. If a has fewer than two distinct values, return None. Do not use sorted.
Note B2. Move the zeros
Return a new list with every zero moved to the end and the order of the other elements unchanged. The list you are given must come back unmodified.
Note B3. Fibonacci
Return a list of the first n numbers of the Fibonacci sequence0, 1, 1, 2, 3, 5, 8, 13, \ldots, in which each number is the sum of the previous two. Decide what n = 0 and n = 1 should give before you write the loop.
ImportantCheckpoint
Stop here for the break. When we come back we look at what broke in part 1 and why, and you explain your fixes to each other. Have an answer ready to: which self-check failed first, and what did its message tell you?
Lab, part 2
Note B4. Digit sum
Return the sum of the digits of an integer. A minus sign is not a digit, so a negative input gives the same answer as its absolute value.
Note B5. Palindrome
Return True if s reads the same forwards and backwards, ignoring case, spaces and punctuation. Only letters and digits count.
Note B6. Leap year
Return True if year is a leap year and False otherwise. The rule has three clauses, not one: 1900 was not a leap year and 2000 was.
Note B7. Letter grades
Write grade(score), returning "A", "B", "C", "D" or "Fail" for a score out of 100:
90–100 → "A"
80–89 → "B"
70–79 → "C"
60–69 → "D"
below 60 → "Fail"
Then write distribution(scores), returning a dict that counts how many scores fall in each grade. All five grades must appear as keys, even when a count is 0.
TipSubmitting
Your lab score comes from what you do in the room, not from this file. Three points: you took both seats in the Part A interview and your partner signed your rubric card; you said something substantive at the checkpoint; and you were working on Part B when I came round and could tell me where you were stuck. None of the three rewards being right.
Hand in your partner’s signed rubric card before you leave. Upload the notebook to Blackboard too—it is the record of what you did, and I will look at it if a grade is ever questioned, but it is not what earns the points.