make_crosstab_line from yesterday formats one line. But what if you need the total response count, group count, and overall average for the header of your methods table — three numbers from one call?
Three separate function calls? Or I could return a dict with three keys. But that feels heavy for just three numbers.
Python lets you return multiple values as a tuple — return a, b, c — and the caller unpacks them: total, groups, avg = summarize_group(responses). Default arguments let you set a sensible fallback:
def summarize_group(responses, field="year_in_school"):
summary = demographic_summary(responses, field)
total = sum(v["count"] for v in summary.values())
group_count = len(summary)
overall_avg = round(sum(v["avg_satisfaction"] * v["count"] for v in summary.values()) / total, 2)
return total, group_count, overall_avg
total, n_groups, avg = summarize_group(responses)
print(f"{total} responses across {n_groups} groups, avg {avg}")field="year_in_school" in the definition — does the caller have to pass it, or is it optional?
Optional. A default argument is used when the caller doesn't provide that argument. summarize_group(responses) uses "year_in_school". summarize_group(responses, "major") overrides it. The default encodes the assumption most callers share — your primary grouping field:
def summarize_group(responses: list, field: str = "year_in_school") -> tuple:
"""Return (total_responses, group_count, overall_avg) for a demographic field."""
summary = demographic_summary(responses, field)
if not summary:
return (0, 0, 0.0)
total = sum(v["count"] for v in summary.values())
group_count = len(summary)
overall_avg = round(sum(v["avg_satisfaction"] * v["count"] for v in summary.values()) / total, 2)
print(f"Total: {total}, Groups: {group_count}, Avg: {overall_avg}")
return total, group_count, overall_avgOne call, three numbers. I can use these directly in the methods section header without another function.
Three numbers, one call, and the field defaults to the column your advisor always wants. That's what good defaults look like.
I called demographic_summary from Day 14 inside this function. Every week's lessons are still alive in this pipeline.
Always guard the empty case. If summary is empty, sum(...) over an empty iterator returns 0 but total / total raises ZeroDivisionError. Return (0, 0, 0.0) early when there's nothing to summarise.
def fn(required_arg, optional_arg="default"):
...fn(x, "override")return a, b, c # returns a tuple (a, b, c)
total, n, avg = fn(...) # unpacks all three at onceOverall average = weighted sum ÷ total count. Averaging the group averages directly is wrong when group sizes differ — a group of 200 responses carries the same weight as a group of 3.
Use if total_responses is None: not if not total_responses: — 0 is falsy but valid input, so the truthiness check would incorrectly trigger the default for a legitimate zero value.
make_crosstab_line from yesterday formats one line. But what if you need the total response count, group count, and overall average for the header of your methods table — three numbers from one call?
Three separate function calls? Or I could return a dict with three keys. But that feels heavy for just three numbers.
Python lets you return multiple values as a tuple — return a, b, c — and the caller unpacks them: total, groups, avg = summarize_group(responses). Default arguments let you set a sensible fallback:
def summarize_group(responses, field="year_in_school"):
summary = demographic_summary(responses, field)
total = sum(v["count"] for v in summary.values())
group_count = len(summary)
overall_avg = round(sum(v["avg_satisfaction"] * v["count"] for v in summary.values()) / total, 2)
return total, group_count, overall_avg
total, n_groups, avg = summarize_group(responses)
print(f"{total} responses across {n_groups} groups, avg {avg}")field="year_in_school" in the definition — does the caller have to pass it, or is it optional?
Optional. A default argument is used when the caller doesn't provide that argument. summarize_group(responses) uses "year_in_school". summarize_group(responses, "major") overrides it. The default encodes the assumption most callers share — your primary grouping field:
def summarize_group(responses: list, field: str = "year_in_school") -> tuple:
"""Return (total_responses, group_count, overall_avg) for a demographic field."""
summary = demographic_summary(responses, field)
if not summary:
return (0, 0, 0.0)
total = sum(v["count"] for v in summary.values())
group_count = len(summary)
overall_avg = round(sum(v["avg_satisfaction"] * v["count"] for v in summary.values()) / total, 2)
print(f"Total: {total}, Groups: {group_count}, Avg: {overall_avg}")
return total, group_count, overall_avgOne call, three numbers. I can use these directly in the methods section header without another function.
Three numbers, one call, and the field defaults to the column your advisor always wants. That's what good defaults look like.
I called demographic_summary from Day 14 inside this function. Every week's lessons are still alive in this pipeline.
Always guard the empty case. If summary is empty, sum(...) over an empty iterator returns 0 but total / total raises ZeroDivisionError. Return (0, 0, 0.0) early when there's nothing to summarise.
def fn(required_arg, optional_arg="default"):
...fn(x, "override")return a, b, c # returns a tuple (a, b, c)
total, n, avg = fn(...) # unpacks all three at onceOverall average = weighted sum ÷ total count. Averaging the group averages directly is wrong when group sizes differ — a group of 200 responses carries the same weight as a group of 3.
Use if total_responses is None: not if not total_responses: — 0 is falsy but valid input, so the truthiness check would incorrectly trigger the default for a legitimate zero value.
You need a summary header for your thesis methods section — total responses, number of demographic groups, and overall average satisfaction — all from one call with sensible defaults. Write `summarize_group(responses, field='year_in_school')` that calls `demographic_summary` and returns `(total_responses, group_count, overall_avg)` as a tuple.
Tap each step for scaffolded hints.
No blank-editor panic.