← Home

158. Chapter 1 Review Quiz

Variables, comments, indentation, keywords, and identifiers

158. Chapter 1 Review Quiz

Aryan finishes Chapter 1 and decides to review everything in one shot. He opens his RAM monitor project and finds five sections of code — each broken in exactly the way from the concept he just studied.

The file has five labelled sections. Fix every bug to pass all the tests.

Section 1 — Variables Six cryptic single-letter names. Rename them to the meaningful names shown in the comments next to each one.

Section 2 — Comments compute_ram_alert has a wrong threshold and no comment explaining it. Fix the threshold to 0.85 for CRITICAL and 0.7 for WARNING. Add a # comment above each if explaining the value. score_session is logically correct but has no comment explaining why problems score 5× and hours score 2×. Add that comment.

Section 3 — Indentation total_memory has a stray line after the loop that zeros the result. count_warnings has a stray line after the loop that undercounts by one. Remove both stray lines.

Section 4 — Keywords check_keyword uses a hardcoded list of 7 words — far too short. Replace it with keyword.iskeyword().

Section 5 — Identifiers validate_field only calls isidentifier() and lets keywords like for pass as valid. Add the keyword exclusion.


💡 Fun fact: Professional code reviews consistently flag the same five classes of mistakes: bad names, missing context comments, scope bugs, incomplete guards, and insufficient validation. You just fixed all five in one file.

⚠️ Watch out: Several tests will pass even with the broken code (e.g., the score_session and some compute_ram_alert cases). A passing test only proves the code works for that input — it doesn’t prove the code is correct.

🤔 Think about it: If a future engineer reads ratio > 0.9 with no comment, what will they assume about the threshold? How likely are they to “round it up” to 1.0 because it seems more natural?

Learning objectives

  • Replace cryptic single-letter names with meaningful domain names
  • Document non-obvious thresholds and weights with inline comments
  • Identify and remove stray lines that corrupt loop results
  • Use keyword.iskeyword() instead of hardcoded keyword lists
  • Combine isidentifier() and iskeyword() for complete identifier validation

Key concepts

  • variables
  • comments
  • indentation
  • keywords
  • identifiers

Try it

Concept detail

This quiz consolidates all five Chapter 1 concepts in a single file:

  1. VARIABLES — Names are free documentation. a, b, c force the reader to reverse-engineer meaning from context. pid, memory_mb, cpu_percent communicate the domain at zero cost. Rename early; rename everywhere.

  2. COMMENTS — Comment the WHY, not the WHAT. ‘# ratio > 0.85’ is noise — the code already shows that. ‘# CRITICAL at 85% per ops runbook v2’ is knowledge that cannot be derived from the code alone. The wrong threshold (0.9 instead of 0.85) is exactly the kind of silent bug that a missing comment enables: no one knows where 0.9 came from, so no one questions it.

  3. INDENTATION — Python scope is indentation. A line at the wrong level is syntactically valid but semantically wrong. ‘total = total * 0’ and ‘count = count - 1’ both run AFTER the loop, corrupting correct results. No IndentationError is raised — the bugs are invisible until a test fails.

  4. KEYWORDS — Python 3 has 35 reserved words. A hardcoded list of 7 misses lambda, assert, yield, async, await, nonlocal, global, and more. keyword.iskeyword() uses the parser’s own definition: always complete, always version-correct.

  5. IDENTIFIERS — str.isidentifier() checks character rules (starts with letter/underscore, only letters/digits/underscores). It returns True for ‘for’, ‘while’, ‘class’ because those are syntactically valid name shapes. The keyword exclusion must be layered on top explicitly: name.isidentifier() and not keyword.iskeyword(name)

Solution

import keyword

# ── SECTION 1: Variables ────────────────────────────────────────────────────
pid = 812
memory_mb = 544
cpu_percent = 6.8

hours_studied = 8
problems_solved = 12
coffee_cups = 3

# ── SECTION 2: Comments ─────────────────────────────────────────────────────
def compute_ram_alert(used_mb, total_mb):
    ratio = used_mb / total_mb
    # CRITICAL at 85% — threshold from ops runbook v2
    if ratio > 0.85:
        return "CRITICAL"
    # WARNING at 70% — early pressure signal
    if ratio > 0.7:
        return "WARNING"
    return "OK"

def score_session(problems, hours):
    # problems weighted 5x, hours weighted 2x per study-score rubric
    return problems * 5 + hours * 2

# ── SECTION 3: Indentation ──────────────────────────────────────────────────
def total_memory(process_list):
    total = 0
    for mem in process_list:
        if mem > 0:
            total += mem
    return total

def count_warnings(log_entries):
    count = 0
    for entry in log_entries:
        if entry == "WARNING":
            count += 1
    return count

# ── SECTION 4: Keywords ─────────────────────────────────────────────────────
def check_keyword(word):
    return keyword.iskeyword(word)

# ── SECTION 5: Identifiers ──────────────────────────────────────────────────
def validate_field(name):
    return name.isidentifier() and not keyword.iskeyword(name)

Tests

# ── Section 1: Variables ────────────────────────────────────────────────────

def test_pid():
    assert pid == 812

def test_memory_mb():
    assert memory_mb == 544

def test_cpu_percent():
    assert cpu_percent == 6.8

def test_hours_studied():
    assert hours_studied == 8

def test_problems_solved():
    assert problems_solved == 12

def test_coffee_cups():
    assert coffee_cups == 3

# ── Section 2: Comments ─────────────────────────────────────────────────────
# Tests verify the corrected threshold logic; the comment itself is learned
# but not machine-checked.

def test_alert_critical_above_85():
    # ratio = 0.88 → above 0.85 → CRITICAL (broken code misses this: returns WARNING)
    assert compute_ram_alert(88, 100) == "CRITICAL"

def test_alert_critical_above_90():
    # ratio = 0.95 → CRITICAL in both broken and fixed
    assert compute_ram_alert(95, 100) == "CRITICAL"

def test_alert_warning_between_70_and_85():
    # ratio = 0.75 → WARNING in both broken and fixed
    assert compute_ram_alert(75, 100) == "WARNING"

def test_alert_ok_below_70():
    # ratio = 0.60 → OK in both broken and fixed
    assert compute_ram_alert(60, 100) == "OK"

def test_score_session_basic():
    # 3 problems * 5 + 2 hours * 2 = 19
    assert score_session(3, 2) == 19

def test_score_session_zero_problems():
    assert score_session(0, 4) == 8

def test_score_session_zero_hours():
    assert score_session(5, 0) == 25

# ── Section 3: Indentation ──────────────────────────────────────────────────

def test_total_memory_all_positive():
    # broken: total * 0 after loop → returns 0; fixed: returns 600
    assert total_memory([100, 200, 300]) == 600

def test_total_memory_skips_zero():
    # 0 values must be excluded by the if condition
    assert total_memory([0, 50, 0, 150]) == 200

def test_total_memory_empty():
    assert total_memory([]) == 0

def test_count_warnings_multiple():
    # broken: count - 1 after loop → returns 1; fixed: returns 2
    assert count_warnings(["OK", "WARNING", "WARNING", "OK"]) == 2

def test_count_warnings_none():
    # broken: 0 - 1 = -1; fixed: returns 0
    assert count_warnings(["OK", "OK"]) == 0

def test_count_warnings_all():
    assert count_warnings(["WARNING", "WARNING", "WARNING"]) == 3

# ── Section 4: Keywords ─────────────────────────────────────────────────────

def test_for_is_keyword():
    assert check_keyword("for") == True

def test_while_is_keyword():
    assert check_keyword("while") == True

def test_lambda_is_keyword():
    # broken: "lambda" not in hardcoded list → returns False
    assert check_keyword("lambda") == True

def test_assert_is_keyword():
    # broken: "assert" not in hardcoded list → returns False
    assert check_keyword("assert") == True

def test_yield_is_keyword():
    # broken: "yield" not in hardcoded list → returns False
    assert check_keyword("yield") == True

def test_async_is_keyword():
    # broken: "async" not in hardcoded list → returns False
    assert check_keyword("async") == True

def test_banana_not_keyword():
    assert check_keyword("banana") == False

def test_print_not_keyword():
    # print is a built-in, not a keyword
    assert check_keyword("print") == False

# ── Section 5: Identifiers ──────────────────────────────────────────────────

def test_my_var_valid():
    assert validate_field("my_var") == True

def test_private_underscore_valid():
    assert validate_field("_private") == True

def test_for_invalid():
    # broken: "for".isidentifier() → True, so broken returns True
    assert validate_field("for") == False

def test_while_invalid():
    assert validate_field("while") == False

def test_lambda_invalid():
    assert validate_field("lambda") == False

def test_digit_start_invalid():
    assert validate_field("1abc") == False

def test_dash_invalid():
    assert validate_field("my-var") == False

def test_space_invalid():
    assert validate_field("my var") == False

Resources