Chapter 0 · Basics of Python
Section 0.4 · Chapter 0 · Learning Statistics with Python
Basics of Python
Prof. Xuhu Wan
ISOM, HKUST Business School · 2026 Edition
A function is a named, reusable block with its own scope. You will predict how defaults, *args, and **kwargs fill in, when a lambda earns its place, why a variable set inside a function vanishes outside, and how try/except turns a crash into a value. Then the mini-project.
def show(x): print(x) then r = show(5). What is r?
5None"5"TypeErrorWhat do the two calls print?
def greet(name, title="Student"):
return f"Hello {title} {name}!"
print(greet("Alex")); print(greet("Alex", title="Dr."))Hello Student Alex! then Hello Dr. Alex!
Defaults are why pd.read_csv(path) works with one argument and pd.read_csv(path, sep=";", index_col=0) with three. You expose sensible defaults and let callers override.
def add_city(c, cities=[]): cities.append(c); return cities. After add_city(“NYC”), what does add_city(“LA”) return?
["LA"][]["NYC", "LA"]TypeError*args: any number of positional valuesmean(first, *nums) averages first with the rest. What is mean(10, 20, 30, 40, 100)?
40.0
10.0 and 40.0. Requiring first guarantees at least one value, so the division can never be by zero. print(...) itself is a *args function — that is why it takes any number of arguments.
**kwargs: any number of keyword optionsConnecting to db.local:5432, ssl=False then Connecting to db.local:6500, ssl=True. **options is a dictionary, so .get(key, default) from §0.2 is exactly the tool. This is how df.plot(**plot_kwargs) forwards styling to matplotlib.
def func(required, defaulted=…, *args, kw_only=…, **kwargs). Predict the middle call.
demo(a, b=10, *args, scale=1.0, **kwargs) returns (a + b + sum(args)) * scale. What is demo(1, 2, 3, 4, scale=0.5)[“total”]?
10.05.011.02.5{'total': 11.0, 'options': {}}, {'total': 5.0, 'options': {}}, {'total': 22.0, 'options': {'theme': 'dark'}}. scale sits after *args, so it can only be passed by name — the pattern behind df.sort_values("x", ascending=False).
lambda: a function with no name[1, 4, 9, 16] twice, then ['LA', 'NYC', 'Chicago']. The key= argument is where lambdas live: sorted, max, min, and pandas’ .apply(lambda r: …). A lambda holds one expression — if you need two lines, write a def.
helpThe triple-quoted string right under def is the docstring; help() (or greet.__doc__) shows it. help(pd.read_csv) is how you read the 50 keyword arguments without leaving the notebook.
x inside?x = “global”; def demo(): x = “local”; return x. What do print(demo()) and print(x) show?
local then globallocal then localglobal then globalUnboundLocalErrorclean_and_count(words)The notebook’s mini-exercise. Write clean_and_count(words): lower-case each word, strip the punctuation .,!? from its ends (w.strip(".,!?")), and return a dict word → count. Use counts.get(w, 0) + 1.
pi = 3.1415926. What does f"{pi:8.3f}" give — including the spaces?
' 3.142'
Maya scored 93.5, 3.14, ' 3.142', 3. Two more you will want for a finance memo: :.2% renders a return as a percentage, :, puts thousands separators in a dollar amount.
zip: walk two sequences togetherlist(zip([“Ana”, “Bo”, “Cy”], [88, 92])) returns?
[("Ana", 88), ("Bo", 92), ("Cy", None)][("Ana", 88), ("Bo", 92)]ValueError[("Ana", "Bo", "Cy"), (88, 92)]try / except: turn a crash into a value5.0 then inf. float("inf") is the IEEE infinity — bigger than any number, so min() over results still works. Catch the specific exception; a bare except: also swallows typos and KeyboardInterrupt.
Which exception does int(“abc”) raise?
KeyErrorTypeErrorIndexErrorValueError| Exception | Raised by | Seen in this chapter |
|---|---|---|
ZeroDivisionError |
x / 0 |
safe_divide |
KeyError |
d["missing"] |
student["gpa"] before it was added |
IndexError |
lst[99] |
slicing past the end |
TypeError |
t[0] = 1, "a" + 1, set()[0] |
tuples, sets |
ValueError |
int("abc") |
converting text |
NameError |
using an undefined name | misspelt variable |
Everything in one place: a string, split, a loop, a dict, .get, a lambda sort, and an f-string. Which word appears most in the paragraph?
Build top3 — the three most frequent words as (word, count) pairs, most frequent first. Clean each word (lower-case, strip .,!?) before counting; the starter forgets to, so Data and data. are counted separately.
Target output: data 4, insight 3, good 2. The same skeleton — clean, count, sort — is a bag-of-words sentiment score on an earnings-call transcript.
return returns None; defaults fill missing arguments, but a mutable default is shared across calls.*args arrives as a tuple, **kwargs as a dict; keyword-only parameters sit after *args.lambda is a one-expression function; its home is key= in sorted, max, .apply.f"{x:8.3f}", :.2%, :, format numbers; zip stops at the shortest input.try/except SpecificError converts a crash into a value you can reason about.Next: Chapter 1 — the pandas Series: a dictionary that learned arithmetic.
Warning
In Growth in a Time of Debt (2010), Carmen Reinhart and Kenneth Rogoff reported that countries with public debt above 90 % of GDP grew at −0.1 % a year on average — a number cited by the UK Treasury, the European Commission and the US Congress to justify austerity after 2010.
In April 2013 Thomas Herndon, a UMass Amherst graduate student, obtained the working spreadsheet for a replication assignment. The averaging formula covered rows 30–44 instead of 30–49: Australia, Austria, Belgium, Canada and Denmark were silently excluded. Combined with a selective country-year sample and an unusual weighting, the corrected average for the >90 % bucket was +2.2 %, not −0.1 % (Herndon, Ash and Pollin, Cambridge Journal of Economics, 2014).
Lesson for this chapter: a range that stops one row early is a slicing error — rows[30:45] when you meant rows[30:50]. Print len() of every slice before you average it, and write the calculation as a function you can test on a five-row example.
The mini-project is a prototype. The deliverable is a recommendation.
To: Head of Equity Research From: <Your name>, research associate Subject: Replace manual keyword tallies on earnings-call transcripts with a Python script Date: 2026-09-15
Recommendation: Adopt the
clean_and_countpipeline for all S&P 500 calls this quarter; keep one analyst on spot-checks.Evidence: - On the 12-transcript pilot the script reproduced the analysts’ counts for 11 of 12 target words; the one miss (
"guidance."vs"guidance") was a punctuation-stripping rule, now fixed. - Run time: 0.4 s per transcript vs ~25 minutes by hand — 500 calls per quarter is ~200 analyst-hours saved.Caveats: - Case-folding merges
"Apple"(the firm) with"apple"(the fruit) — acceptable for finance vocabulary, wrong for names. - Counting words ignores negation (“not confident”); this is a count, not a sentiment score.Next step: Wrap the counts in a pandas
DataFrame(Chapter 2) and test whether the word rate predicts next-day returns (Chapter 4).
An LLM writes fluent Python. It also writes plausible Python that fails on exactly the traps in this chapter.
add_city(c, cities=[])” will be accepted as-is. Instead: “Write it and tell me whether any default argument is mutable, and why that matters.”clean_and_count starter and say: “Give me three input/expected-output pairs I can run before trusting this.” If the copilot cannot produce the expected output for ["Data", "data."], it does not understand the cleaning rule either.try: … except: pass. Ask “which specific exception are you catching, and what value do you return instead?” — if the answer is “all of them”, reject the patch.| Concept | Tool |
|---|---|
| Arithmetic and division | + - * / // % **; / → float, // floors |
| Types and conversion | type(), int(), float(), str(), bool() |
| Sequences | s[start:stop:step], len(), in |
| Strings | .strip(), .upper(), .replace(), .split(), immutable |
| Lists / tuples | .append(), .insert(), .pop(), .copy(); (a, b) immutable |
| Sets / dicts | set(), .add(), .union(); d[k], d.get(k, default), .items() |
| Control flow | if/elif/else, for, enumerate, range, while, break, continue |
| Comprehensions | [f(x) for x in xs if c], {k: v for …} |
| Functions | def, defaults, *args, **kwargs, lambda, docstrings, scope |
| Text and errors | f"{x:.2f}", zip, try/except SpecificError |
Next: Chapter 1 — Data Structures and Methods of Series.
b = a for a list and b = a for an integer behave differently when you then “change b”. Explain, using the words object, name, and immutable, why there is in fact no difference in what = does.prices[i:i+19]. What is wrong, how would you catch it with one print, and what does the Reinhart–Rogoff episode add to the argument?d[key] over d.get(key, default) in production code, given that only one of them can crash your pipeline at 3 a.m.?collections.Counter. What did you gain, and what did the beginner reading your code lose?Prof. Xuhu Wan · HKUST ISOM · Learning Statistics with Python