Skip to content

Functions

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.

TypeScript
// TypeScript
function 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
Python
# Python
def 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)) # 7

Key differences to notice:

  • def keyword instead of function.
  • Return type annotation after -> instead of : ReturnType.
  • No curly braces — the function body is indented.
  • No semicolons.

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
// TypeScript — must use object destructuring to name args
function createUser({
name,
role = "viewer",
active = true,
}: {
name: string;
role?: string;
active?: boolean;
}) {
console.log(`${name} (${role}), active=${active}`);
}
createUser({ name: "Alice", role: "admin" });
Python
# Python — every parameter is callable by name
def create_user(name: str, role: str = "viewer", active: bool = True) -> None:
print(f"{name} ({role}), active={active}")
# Positional call
create_user("Alice", "admin")
# Keyword call — order does not matter
create_user(role="admin", name="Alice")
# Mix positional and keyword
create_user("Bob", active=False)

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.

TypeScript
// TypeScript
function sum(...numbers: number[]): number {
return numbers.reduce((a, b) => a + b, 0);
}
// No clean native equivalent for named variadic args
function 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)); // 10
logFields({ lang: "Python", tier: "backend" });
Python
# Python *args and **kwargs
def 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.0
log_fields(lang="Python", tier="backend")

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
// TypeScript — return an object or array
function 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
# Python — return a tuple, unpack at the call site
def 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 tuple
result = min_max([3, 1, 4, 1, 5, 9])
print(result) # (1, 9)
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.12", tier="backend")
What keyword does Python use to define a function?
In Python, calling `greet(name="Alice", greeting="Hey")` despite declaring `def greet(greeting, name)` is valid because of:
What does `**kwargs` collect inside a Python function?
What does `def divmod_custom(a, b): return a // b, a % b` return?