# Lesson 0: Python survival kit ## Why you care In SAS, you press F3 in a SAS session to run your program. In this course, you run script files from a terminal, and the scripts grade themselves with assertions. Everything in lessons 2–6 assumes the mechanics taught here. ## Running a script A `.py` file is exactly like a `.sas` program — it's a text file containing Python code. To run it, activate the venv and call `python path/to/file.py` from the repo root. Let's try it. Create a file called `scratch.py` in the repo root (not in any subdirectory) with exactly this line: ```python print("hello from python") ``` Then run: ```bash python scratch.py ``` Output: ``` hello from python ``` When you're done, `scratch.py` is yours to delete; it's not part of the course and is ignored by git. For platform-specific venv activation commands (macOS/Linux vs. Windows Git Bash), see the README section [How to run the scripts](../README.md#how-to-run-the-scripts). ## Imports and aliases In SAS, you reference a library and give it a nickname (like `PROC SQL; CREATE LIBRARY mylib = "path/to/data";`). In Python, `import polars as pl` loads the Polars library and nicknames it `pl` so you can write `pl.something` instead of `polars.something`. This is just a convention — you can use any nickname, but `pl` for Polars is standard. Create `scratch.py`: ```python import polars as pl print(pl.__version__) ``` Run it: ```bash python scratch.py ``` Output: ``` 1.42.1 ``` That's the Polars version installed in this repo. ## Variables and print Assignment has no `%LET` or data-step split; `name = value` creates a variable, and `print()` displays any value. Create `scratch.py`: ```python n = 42 name = "Alice" print(n) print(name) ``` Run it: ```bash python scratch.py ``` Output: ``` 42 Alice ``` ## f-strings An f-string is `f"..."` — the `f` prefix lets you embed expressions inside `{}`. This is like SAS `PUT` or `%PUT` statements, but more flexible. Inside `{}`, you can format numbers: `{n:,}` adds commas as thousands separators. Create `scratch.py`: ```python n = 199613 print(f"rows: {n:,}") ``` Run it: ```bash python scratch.py ``` Output: ``` rows: 199,613 ``` ## Functions and the `__main__` guard Every exercise in this course uses this shape: ```python def main() -> None: # your work here print("All checks passed.") if __name__ == "__main__": main() ``` Here's what each part does: - `def main() -> None:` defines a function called `main` that takes no arguments and doesn't return anything (the `-> None` is just a label for clarity). - `if __name__ == "__main__": main()` runs `main()` only when the file is executed directly (not when another file imports it). This is trusted boilerplate — use it exactly as shown, and don't worry further about how it works. ## Method chaining In SAS, you run PROC steps in sequence. In Polars, you chain method calls: `df.filter(...).select(...)` reads left to right like a pipeline. Each call returns a new DataFrame, so you can break chains across lines inside parentheses. Create `scratch.py`: ```python import polars as pl result = pl.DataFrame({"x": [1, 2, 3]}).filter(pl.col("x") > 1).height print(result) ``` Run it: ```bash python scratch.py ``` Output: ``` 2 ``` The chain: create a DataFrame → filter rows where `x > 1` (leaves rows with `x=2` and `x=3`) → get the row count (`.height`). Result: 2 rows. ## How the course grades you: assertions and tracebacks Every exercise ends with assertions. When you run the file, Python checks each `assert` statement. If the condition is true, nothing happens. If it's false, Python raises `AssertionError` and stops. You then read the traceback (the error message) to find the problem and fix it. ### How to read a traceback A traceback is read **bottom-up**. The last line names the error type and message. The lines above show where the error happened (file, line number, and code). ### NotImplementedError: you haven't started the TODO yet Some exercises contain `raise NotImplementedError("...")` as placeholders for work you haven't done. When you run the file before filling in the blank, you get this. Create `scratch.py`: ```python raise NotImplementedError("TODO 1: not started yet") ``` Run it: ```bash python scratch.py ``` Output (read bottom-up): ``` Traceback (most recent call last): File "scratch.py", line 1, in raise NotImplementedError("TODO 1: not started yet") NotImplementedError: TODO 1: not started yet ``` The last line (`NotImplementedError: TODO 1: not started yet`) tells you: this TODO hasn't been started. The line before (`raise NotImplementedError(...)`) shows exactly where, and the file/line info (`File "scratch.py", line 1`) points you to it. ### AssertionError: you did the TODO, but the result is wrong When an assertion fails, Python shows the code that failed and the custom message you wrote. Create `scratch.py`: ```python x = 2 + 2 assert x == 5, f"expected 5, got {x}" ``` Run it: ```bash python scratch.py ``` Output (read bottom-up): ``` Traceback (most recent call last): File "scratch.py", line 2, in assert x == 5, f"expected 5, got {x}" ^^^^^^ AssertionError: expected 5, got 4 ``` The last line (`AssertionError: expected 5, got 4`) tells you what you got wrong. The `^^^^^^` marker under the code shows exactly which part of the assertion failed. ### NameError or AttributeError: you missed a blank Exercises 2–3 contain blanks: `____` appears where you need to fill in a variable name, method, or attribute. If you run before filling a blank, you get either `NameError` (for bare identifiers) or `AttributeError` (for method/attribute positions). Create `scratch.py`: ```python print(____) ``` Run it: ```bash python scratch.py ``` Output (read bottom-up): ``` Traceback (most recent call last): File "scratch.py", line 1, in print(____) ^^^^ NameError: name '____' is not defined ``` The last line tells you a name isn't defined. The line before shows which name: `____`. Go back to the exercise and replace `____` with the correct name. ### Success: all assertions pass When every `assert` is true, you see `All checks passed.` and nothing else bad. Create `scratch.py`: ```python x = 2 + 2 assert x == 4, f"expected 4, got {x}" print("All checks passed.") ``` Run it: ```bash python scratch.py ``` Output: ``` All checks passed. ``` ## Check yourself **Question 1:** What does this print? ```python print(f"{10_000:,}") ```
Answer `10,000` The `{10_000:,}` format adds commas as thousands separators. Underscores in numeric literals (like `10_000`) are just visual separators in code — they don't change the number itself.
**Question 2:** What error do you get if you run a file whose first statement is `raise NotImplementedError("TODO 2")`?
Answer ``` Traceback (most recent call last): File "scratch.py", line 1, in raise NotImplementedError("TODO 2") NotImplementedError: TODO 2 ``` The error type is `NotImplementedError`, with the message you provided. Read the traceback bottom-up: the last line tells you the error and its message.
**Question 3:** If you run `assert 3 > 2`, does it print anything?
Answer No. The condition `3 > 2` is true, so the assertion passes silently. Assertions only produce output when they fail.
## Next Next lesson: [01_parquet_in_vscode.md](01_parquet_in_vscode.md)