Before you begin
Install Python 3 from python.org. Open a terminal and run python3 --version (Windows: py -3 --version). Use a plain-text editor. Save each example in its own .py file; run it from that folder. No third-party packages are needed for these lessons.
Working toward: Build a command-line field-notes processor with validated input, tests, and a distributable package.
Read each explanation, run the example in your own lab, and attempt the exercise before opening its answer. Published lessons are ready to study; unfinished roadmap topics remain planned.
Validation: Lessons 1–4 retain their prior expected-output checks. Lesson 5 was executed on Linux with Python 3.12.14: normal, zero, invalid and missing arguments, --help, abbreviated-option rejection, and the exercise solution. Python 3.14 documentation was reviewed; 3.14 itself and Windows were not execution-tested.
1. Values, names, and output
A program is a sequence of instructions. A variable is a name referring to a value. Here nights and nightly_rate refer to integers; multiplication produces another integer. print sends text to your terminal. An f-string evaluates expressions inside braces.
nights = 7
nightly_rate = 80
total = nights * nightly_rate
print(f"Accommodation: {total} EUR")Run it
python3 budget.py
# Windows: py -3 budget.pyWhat to expect
Accommodation: 560 EUR
Your turn
Add a daily food allowance of 25 EUR for seven days and print the combined budget.
Show answer and reasoning
food = 25 * nights
print(f"Total: {total + food} EUR")
# Total: 735 EURWatch for: "7" is text; 7 is a number. Multiplying text repeats it. Quotes matter, and capitalization distinguishes names.
Link to this lesson2. Decisions and repeated work
A list holds an ordered collection. A for loop visits each item. if chooses a branch based on a Boolean condition. Indentation groups instructions: the print below belongs to the if, which belongs to the loop.
costs = [8, 24, 13, 31]
for cost in costs:
if cost > 20:
print(f"Review: {cost}")What to expect
Review: 24, then Review: 31, on separate lines.
Your turn
Print only costs between 10 and 25 inclusive.
Show answer and reasoning
for cost in costs:
if 10 <= cost <= 25:
print(cost)
# 24 and 13Watch for: An equals sign assigns a value. == compares values. Mixing tabs and spaces can break indentation.
Link to this lesson3. Functions and checks
A function names a reusable calculation. Parameters receive inputs; return supplies a result to the caller. Keeping calculation separate from printing makes it easier to test. This example checks for a negative count before calculating.
def trip_total(days, daily):
if days < 0:
raise ValueError("days must be nonnegative")
return days * daily
assert trip_total(3, 12) == 36
assert trip_total(0, 12) == 0
print(trip_total(7, 25))What to expect
175; assertions produce no output when they pass.
Your turn
Allow a fixed transport charge as a third parameter, defaulting to zero. Check both default and explicit values.
Show answer and reasoning
def trip_total(days, daily, transport=0):
if days < 0:
raise ValueError("days must be nonnegative")
return days * daily + transport
assert trip_total(3, 12) == 36
assert trip_total(3, 12, 5) == 41Watch for: assert is useful for learning checks but can be disabled. Validate user input with explicit conditions and exceptions.
Link to this lesson4. Read structured data
CSV gives each record a row and each field a column. DictReader uses the first row as field names. A with block closes the file after use. Convert the amount column to an integer before adding; this fixture stores whole euros.
import csv
with open("expenses.csv", newline="", encoding="utf-8") as source:
rows = csv.DictReader(source)
total = sum(int(row["euros"]) for row in rows)
print(total)Run it
Create expenses.csv with these three lines:
item,euros
lunch,12
taxi,18What to expect
30
Your turn
Add a coffee row for 4 euros. Then try a blank amount and identify the exception before deciding how the program should report it.
Show answer and reasoning
The total becomes 34. int("") raises ValueError. A useful next revision catches it and reports the row number; silently treating malformed amounts as zero hides bad data.Watch for: File paths are relative to your terminal’s working directory. Do not install packages into a system Python just to solve a missing-file error.
Link to this lesson5. Accept command-line input without trusting it
Before this lesson: Complete values, decisions and functions (lessons 1–3); lesson 4's CSV work is useful context but not required. Use Python 3.12 or newer in a new, empty lab folder. No packages, files of personal data, or network access are needed. Outcome: build a CLI that explains its inputs, rejects bad values and returns a predictable result.
A command-line interface (CLI) receives text after a program name. A positional argument is identified by its location; an option has a name such as --daily-cents. Parsing identifies these pieces. Validation decides whether their values make sense for this program. Integer conversion alone accepts negative numbers, so it does not enforce our nonnegative-budget rule.
The contract here is explicit: days and daily cents must be whole numbers at least zero. Money is represented as integer cents, not floating-point euros. Our examples use EUR; this is not a currency converter. Zero days and a zero rate are legitimate boundary cases. A missing required argument is different from an explicitly supplied zero.
argparse builds help, converts text, and reports usage errors. The type function receives one text value. int may raise ValueError if conversion fails; try/except catches just that failure and raises a clearer ArgumentTypeError. A second check rejects negatives. The parser calls the converter before handing a Namespace (a small object of named values) to the calculation. Attributes such as args.daily_cents correspond to option names with hyphens changed to underscores.
Save the complete program below as budget_cli.py. The main function keeps parsing and printing together; the __name__ guard runs it when this file is executed, but not when it is imported. allow_abbrev=False requires complete long option names, so later additions cannot change the meaning of an abbreviated command. Successful output belongs on stdout; argparse sends invalid-input diagnostics to stderr and exits with status 2. --help exits successfully without calculating.
import argparse
def nonnegative_int(text):
try:
value = int(text)
except ValueError:
raise argparse.ArgumentTypeError("use a whole number") from None
if value < 0:
raise argparse.ArgumentTypeError("must be zero or greater")
return value
def main():
parser = argparse.ArgumentParser(
description="Estimate a daily budget in integer EUR cents.",
allow_abbrev=False,
)
parser.add_argument("days", type=nonnegative_int, help="number of days")
parser.add_argument(
"--daily-cents", type=nonnegative_int, required=True,
help="daily amount in EUR cents",
)
args = parser.parse_args()
total = args.days * args.daily_cents
print(f"Total: {total} EUR cents")
if __name__ == "__main__":
main()Run it
python3 budget_cli.py 7 --daily-cents 2500
python3 budget_cli.py 0 --daily-cents 2500
python3 budget_cli.py --help
python3 budget_cli.py -1 --daily-cents 2500
python3 budget_cli.py 7 --daily-cents tea
python3 budget_cli.py 7
# Windows: replace python3 with py -3.
# Observe a command's exit status immediately afterward:
# Bash: echo $?
# PowerShell: $LASTEXITCODE
# CMD: echo %ERRORLEVEL%What to expect
The first two commands print Total: 17500 EUR cents and Total: 0 EUR cents; each exits 0. Help lists days and --daily-cents and exits 0. Negative days produce 'must be zero or greater'; tea produces 'use a whole number'; the final command reports the required --daily-cents option. These three failures exit 2 and print no total. Help layout and color can vary: Python 3.14 introduced color and suggestion settings, which this cross-version example does not require.
Your turn
Add --transport-cents, using the same validation, with a default of 0. Include it once in the total, not once per day. Before running, predict results for (a) 3 days at 1200 with no transport, (b) the same plus 500 transport, (c) 0 days plus 500 transport, and (d) -1 transport. Then try 1.5 days and --daily 1200. Explain why both should fail.
Show answer and reasoning
# Add before args = parser.parse_args():
parser.add_argument(
"--transport-cents", type=nonnegative_int, default=0,
help="one-time transport amount in EUR cents (default: 0)",
)
# Replace the total calculation:
total = args.days * args.daily_cents + args.transport_cents
# Predictions:
# (a) 3600; (b) 4100; (c) 500 EUR cents, all status 0.
# (d) status 2, no total: the shared converter rejects negatives.
# 1.5 is not an integer; --daily is not the full option name.
# The fixed charge is added after multiplication. Zero days does
# not waive it: that is the contract, not a side effect of parsing.
# Ready to move on: explain each failure and add a validated
# --entry-cents fixed charge without looking at this answer.Watch for: Do not use eval to turn input into a number or assert to enforce this contract. Do not catch every exception and replace it with zero: that can make broken data look valid. This CLI validates its own boundary; if a calculation becomes a reusable public function later, decide and test that function's contract too. The accepted integer syntax is Python int's syntax, not a digits-only policy.
Lesson references
- Python argparse: type conversion, error handling, exit status and 3.14 changes (checked 14 September 2026) ↗
- Python exceptions: try, except and raising errors ↗
- Python __main__: the top-level execution environment ↗
Path to advanced
In-progress stages identify the lessons already published. All other listed topics remain planned. Each addition needs teaching, a reproducible lab, failure cases, and a checkpoint before the capstone.
Reliable scripts
Published: validated command-line arguments (lesson 5). Planned: broader exception handling, pathlib, logging, virtual environments, and automated test suites.
IN PROGRESS · PUBLISHED LESSONS ABOVEWorking with data
JSON, HTTP timeouts, SQLite, Unicode, generators, and type hints.
PLANNEDSoftware engineering
Modules, packaging, dependency management, CI, profiling, and concurrency.
PLANNEDCapstone
Deliver a documented corpus-cleaning CLI with tests, sample data, and reproducible installation.
PLANNED
References
Original AUWEN lessons, with upstream documentation for further study and version checks.
All learning paths and update notes →