Functions
Functions: def instead of function
Section titled “Functions: def instead of function”You already know functions well from TypeScript. Python’s def keyword maps almost directly onto TS’s function declaration — same ideas, slightly different syntax. Where Python diverges is in its keyword arguments system and the ability to return multiple values cleanly via tuple unpacking.
Basic function definition
Section titled “Basic function definition”// TypeScriptfunction greet(name: string, greeting: string = "Hello"): string { return `${greeting}, ${name}!`;}
const add = (a: number, b: number): number => a + b;
console.log(greet("Alice")); // Hello, Alice!console.log(greet("Bob", "Hi")); // Hi, Bob!console.log(add(3, 4)); // 7# Pythondef greet(name: str, greeting: str = "Hello") -> str: return f"{greeting}, {name}!"
def add(a: int, b: int) -> int: return a + b
print(greet("Alice")) # Hello, Alice!print(greet("Bob", "Hi")) # Hi, Bob!print(add(3, 4)) # 7Key differences to notice:
defkeyword instead offunction.- Return type annotation after
->instead of: ReturnType. - No curly braces — the function body is indented.
- No semicolons.
Keyword arguments
Section titled “Keyword arguments”Python lets callers name any argument at the call site, regardless of position. This is not the same as TypeScript’s object-destructuring pattern — in Python, every parameter is inherently callable by name.
// TypeScript — must use object destructuring to name argsfunction createUser({ name, role = "viewer", active = true,}: { name: string; role?: string; active?: boolean;}) { console.log(`${name} (${role}), active=${active}`);}
createUser({ name: "Alice", role: "admin" });# Python — every parameter is callable by namedef create_user(name: str, role: str = "viewer", active: bool = True) -> None: print(f"{name} ({role}), active={active}")
# Positional callcreate_user("Alice", "admin")
# Keyword call — order does not mattercreate_user(role="admin", name="Alice")
# Mix positional and keywordcreate_user("Bob", active=False)*args and **kwargs: variadic arguments
Section titled “*args and **kwargs: variadic arguments”Python’s *args collects extra positional arguments into a tuple. **kwargs collects extra keyword arguments into a dict. Together they let you write flexible APIs.
// TypeScriptfunction sum(...numbers: number[]): number { return numbers.reduce((a, b) => a + b, 0);}
// No clean native equivalent for named variadic argsfunction logFields(fields: Record<string, string>): void { for (const [k, v] of Object.entries(fields)) { console.log(`${k}: ${v}`); }}
console.log(sum(1, 2, 3, 4)); // 10logFields({ lang: "Python", tier: "backend" });# Python *args and **kwargsdef total(*numbers: float) -> float: return sum(numbers)
def log_fields(**fields: str) -> None: for key, value in fields.items(): print(f"{key}: {value}")
print(total(1, 2, 3, 4)) # 10.0log_fields(lang="Python", tier="backend")Returning multiple values
Section titled “Returning multiple values”TypeScript uses destructuring on arrays or objects. Python uses tuple unpacking — the function returns a tuple and you unpack it directly. This feels like a first-class language feature rather than a workaround.
// TypeScript — return an object or arrayfunction minMax(nums: number[]): { min: number; max: number } { return { min: Math.min(...nums), max: Math.max(...nums) };}
const { min, max } = minMax([3, 1, 4, 1, 5, 9]);console.log(min, max); // 1 9# Python — return a tuple, unpack at the call sitedef min_max(nums: list[float]) -> tuple[float, float]: return min(nums), max(nums)
low, high = min_max([3, 1, 4, 1, 5, 9])print(low, high) # 1 9
# Or keep it as a tupleresult = min_max([3, 1, 4, 1, 5, 9])print(result) # (1, 9)Generic functions: type parameters in the signature
Section titled “Generic functions: type parameters in the signature”TypeScript puts type parameters in angle brackets after the function name. Since Python 3.12 (PEP 695) Python puts them in square brackets in the same place — no TypeVar import, no module-level declaration.
// TypeScriptfunction first<T>(items: T[]): T | undefined { return items[0];}
type Pair<T> = [T, T];
const n = first([1, 2, 3]); // number | undefinedconst s = first(["a", "b"]); // string | undefined# Python 3.12+def first[T](items: list[T]) -> T | None: return items[0] if items else None
type Pair[T] = tuple[T, T]
n = first([1, 2, 3]) # int | Nones = first(["a", "b"]) # str | None
print(first([1, 2, 3]), first(["a", "b"]), first([]))Older code you will still meet declares the variable separately with T = TypeVar("T") and annotates with List[T] from typing. Both still work; the bracket syntax is the one to write in new code.
Try it
Section titled “Try it”def greet(name: str, greeting: str = "Hello") -> str: return f"{greeting}, {name}!"
def stats(*numbers: float) -> tuple[float, float]: return min(numbers), max(numbers)
def describe(**info: str) -> None: for key, value in info.items(): print(f" {key}: {value}")
print(greet("Alice"))print(greet("Bob", greeting="Hi"))print(greet(name="Carol", greeting="Hey"))
low, high = stats(3, 1, 4, 1, 5, 9, 2, 6)print(f"min={low}, max={high}")
print("User info:")describe(language="Python", version="3.14", tier="backend")Loading Python runtime (first run only)…