Python Essentials for AI Engineer

Course Content

Python Essentials for AI Engineer

6 sections · 48 lessons

What are default arguments in Python?


What you need to know

Syntax and the ordering rule

Python
def load(path, encoding="utf-8", limit=None):    return f"{path} {encoding} {limit}"print(load("txns.csv"))                        # txns.csv utf-8 Noneprint(load("txns.csv", limit=100))             # txns.csv utf-8 100print(load("old.csv", encoding="latin-1"))     # old.csv latin-1 None

def f(a=1, b): is a SyntaxError, because Python could not tell which positional argument goes where. Required parameters come first, then those with defaults. Passing defaults by keyword (limit=100) lets you skip the ones in between.

Defaults are evaluated once

When Python executes the def line, it evaluates each default expression and stores the result on the function object (you can see it in load.__defaults__). Every later call reuses that stored object. This is fine for immutable values and surprising for anything else:

Python
from datetime import datetimeimport timedef log_event(msg, at=datetime.now()):      # evaluated ONCE, at definition    return f"{at:%H:%M:%S} {msg}"first = log_event("start")time.sleep(1.1)second = log_event("end")print(first[:8] == second[:8])              # True -> same timestamp twice

The fix is the None pattern: def log_event(msg, at=None): and inside, if at is None: at = datetime.now().

"Not passed" vs "passed None"

Sometimes None is a meaningful value the caller might pass. Then use a private sentinel — a unique object that no caller will ever pass:

Python
_MISSING = object()def set_timeout(seconds=_MISSING):    if seconds is _MISSING:        return "use default 30s"    if seconds is None:        return "no timeout"    return f"{seconds}s"print(set_timeout(), "|", set_timeout(None), "|", set_timeout(5))# use default 30s | no timeout | 5s

A real-life example

A team's LLM helper has sensible defaults so most calls need only the prompt:

Python
def call_llm(prompt, model="chat-small", temperature=0.2, max_tokens=512, timeout=30.0):    return model, temperature, max_tokensprint(call_llm("Summarise this ticket"))                 # ('chat-small', 0.2, 512)print(call_llm("Grade this answer", temperature=0.0))    # ('chat-small', 0.0, 512)

The support bot uses the defaults; the evaluation job overrides temperature=0.0 for repeatable grading. Changing the default model for the whole codebase is a one-line change. Before that, the same helper had request_time=datetime.now() as a default, and every request logged for a week carried the server's start-up time — which made a latency investigation useless until someone spotted it.

Follow-up questions to expect

  • "When is a default value evaluated?" — Once, when the def statement executes. The resulting object is reused on every call.
  • "Why must defaults come after required parameters?" — Positional arguments fill parameters left to right; a required parameter after an optional one would be ambiguous.
  • "How do you tell whether the caller passed None?" — Use a private sentinel object as the default and check is _MISSING.