You've already read this line in the wild: pd.read_csv('data.csv', sep=';', header=0). One required argument, two NAMED ones, and a dozen more silently using sensible defaults. That calling style is not pandas magic — it's two plain Python features you're about to own.
What you'll learn
What
A default argument gives a parameter a fallback value in the def, making it optional at the call. A keyword argument is passed as name=value, matching by NAME instead of position.
Why
Real functions accumulate options. Without defaults, every caller passes everything, every time. Without keywords, calls become unreadable positional soup — round(3.14159, 2) is fine, but plot(data, True, False, 0.8, 'red') is a quiz. Defaults + keywords are how Python APIs stay humane at scale.
Where it's used
Every data library call you'll ever write: read_csv's 50 optional parameters, train_test_split(test_size=0.2), plt.plot(alpha=0.5), sorted(key=..., reverse=True).
Where this runs in production
read_csv has one required parameter and roughly fifty optional ones — sep=',', header='infer', encoding=None. Millions of users pass only what they need; defaults do the rest.
SDK calls like client.messages.create(model=..., max_tokens=1024, temperature=0.7) rely on keyword arguments so code reads as configuration, not a puzzle of positions.
plt.plot(x, y, color='steelblue', linewidth=2, alpha=0.8) — positional data, keyword styling. The split you'll use in the visualization course daily.
def format_price(amount, currency='USD', decimals=2):
return f'{amount:.{decimals}f} {currency}'
print(format_price(49.5)) # both defaults
print(format_price(49.5, 'EUR')) # positional override
print(format_price(49.5, decimals=0)) # keyword skips currencyThree call styles against one definition. The last line is the point: keywords let you override the SECOND default without touching the first — impossible with positions alone.
A café order form has defaults: medium size, whole milk, no extra shot. Regulars just say 'a latte' — defaults apply. Custom orders name only what changes: 'a latte, oat milk'. Nobody recites every option in a fixed order, and the barista never guesses which unlabeled word meant the milk. Keyword arguments are ordering coffee like a human.
split_data(df, test_size=0.2, shuffle=True) and split_data(df, shuffle=True, test_size=0.2) are identical calls. Naming eliminates the silent-swap bug from last lesson AND documents the call site: six months later, test_size=0.2 still explains itself where a bare 0.2 wouldn't. Convention: positional for the obvious core data, keywords for options.
Default values are evaluated ONCE, at definition time — not per call. Harmless for numbers and strings, but `def add_row(row, table=[]):` creates ONE shared list reused by every call that omits table: rows from different calls pile into the same list. The standard fix: default to None and create fresh inside — `def add_row(row, table=None): if table is None: table = []`. This is a favorite interview question because it looks like magic until you know the one-evaluation rule.
One call — report(sales, 'Q3', top_n=5) — resolved step by step. Click each node.
Select a type to see its full definition, operations, and data science usage.
A greeting utility where the greeting word is optional.
name is required; greeting falls back to 'Hello'. Required-then-default order is mandatory.
Your task
Build a notification formatter for an alerting system. Define format_alert(message, level='INFO', repeat=1) that uppercases the level, prefixes it in brackets, and repeats the line `repeat` times separated by newlines. Produce the three outputs shown using defaults and keywords appropriately.
[INFO] backup finished [WARN] disk almost full [INFO] service down [INFO] service down
Write your solution in the editor on the right, then hit Run.
Which definition is a SyntaxError?
def resize(img, width=100, height=100): ... — which call sets ONLY height to 50?
What does this print? def append_log(entry, log=[]): log.append(entry) return log append_log('a') print(append_log('b'))
What convention prevents this?
Define paginate(items, page_size=3) that returns the FIRST page (a list of at most page_size items). Print paginate([1,2,3,4,5,6,7]) and paginate([1,2,3,4,5,6,7], page_size=5). Expected: [1, 2, 3] [1, 2, 3, 4, 5]
def f(a, b=2): ... — what does f(1, a=3) raise?
Which change keeps all 80 call sites working AND why?
Explain the mutable default argument problem and the standard fix.
Default values are evaluated exactly once, when the def statement executes — not on each call. For immutable defaults (numbers, strings, None) that's invisible. But a mutable default like [] or {} becomes a single object SHARED by every call that omits the argument: append to it in one call and the next call sees the leftovers, so state leaks between supposedly independent calls. The standard fix is the None sentinel: declare param=None, and inside the body write `if param is None: param = []` to build a fresh object per call. The follow-up worth volunteering: the same one-evaluation rule means a default like timestamp=now() freezes the time at import — dynamic defaults always belong inside the body.
When do you pass arguments positionally versus by keyword? What convention do major libraries follow?
The working convention: positional for the essential data whose meaning is unmistakable — the DataFrame, the file path, the (x, y) pair — and keywords for everything optional or configuration-like. Keywords buy three things: the call documents itself (test_size=0.2 needs no comment), argument order can't silently swap same-typed values, and you can override the fifth option without restating the four before it. Libraries like pandas and scikit-learn follow exactly this — read_csv(path, sep=';') — and increasingly enforce it by making options keyword-only, so plot(data, True, False) simply won't run. My personal rule: any boolean or any second same-typed argument gets a name.
You need to add an option to a function with dozens of existing call sites. Walk me through doing it safely.
Add a new parameter with a default that exactly reproduces the current behavior — fetch_data(url, use_cache=False) where all existing behavior corresponds to False. Every current call continues to compile and behave identically because the default fills the gap; only new call sites name the option to opt in. That's backwards-compatible API evolution, and it's how read_csv accumulated fifty options over a decade without a breaking release. Things I'd check in review: the default really is behavior-preserving (not just plausible), the new parameter goes after existing ones so positional callers are unaffected, and documentation states the default. The anti-patterns to name: creating fetch_data_v2 (forks maintenance) and changing the parameter order (breaks every positional call silently).
Common Mistakes to Avoid
1) Mutable defaults (=[] or ={}) — shared across calls; use the None sentinel. 2) Required parameter after a defaulted one — SyntaxError at definition. 3) Positional arguments after keyword ones in a call — SyntaxError. 4) Passing a value both positionally and by keyword — 'multiple values' TypeError. 5) Boolean flags passed positionally (f(df, True, False)) — legal, unreadable; name them. 6) Dynamic defaults evaluated at def time (timestamp=now()) — compute inside the body instead.
Ask the AI Tutor
Try these prompts in the AI Tutor panel: • 'Quiz me on which of these six calls are legal and what they bind.' • 'Walk me through the mutable default trap with a fresh example.' • 'Show me read_csv's signature and explain five of its defaults.' • 'Help me redesign this positional-soup function signature.' • 'Interview mode: ask me how to evolve an API without breaking callers.'
Glossary
Default argument — a fallback value declared in the def, making the parameter optional. Keyword argument — an argument passed as name=value, matched by name. Positional argument — matched by order. Signature — a function's name plus its parameter list; the contract callers read. None sentinel — defaulting to None and creating the real (mutable) value inside the body. Keyword-only parameter — one that callers MUST name (enforced with * in the signature — you'll meet it next lesson). Backwards compatibility — new versions keeping old calls working. API surface — everything callers can see and depend on.
Recommended Resources
• Docs: 'Default Argument Values' and 'Keyword Arguments' in the official Python tutorial. • Read: the pandas read_csv API page — count how many defaults you now understand at a glance. • Practice: take a three-flag function you've written and convert every call site to keywords; feel the readability change. • Next in DSM: what if a function should accept ANY number of arguments? *args and **kwargs — the packing/unpacking machinery behind flexible signatures.
Recap
✓ Defaults in the def make parameters optional; required parameters always come first. ✓ Keywords match by name — order-proof, self-documenting, and able to skip earlier defaults. ✓ Positional for core data, keywords for options: the convention every major library follows. ✓ Mutable defaults are evaluated once and shared — use the None sentinel. ✓ Adding a behavior-preserving default is how APIs evolve without breaking callers. ✓ One value per parameter; positionals before keywords in every call. Next up: *args and **kwargs. Some functions must accept any number of arguments — packing and unpacking give you variable-length signatures and the ** trick you'll meet in every framework.
Run your code to see the output here.