04.1 - Defining & Calling Functions
What is a Function?
A function is a reusable, named block of code that:
- Takes inputs (parameters)
- Performs an operation
- Returns an output (return value)
# Anatomy of a function
def greet(name: str, greeting: str = "Hello") -> str:
"""Return a greeting message.
Args:
name: The person's name.
greeting: The greeting word (default: Hello).
Returns:
A formatted greeting string.
"""
return f"{greeting}, {name}!"
# Call the function
message = greet("Alice") # "Hello, Alice!"
message = greet("Bob", "Hi") # "Hi, Bob!"
def greet (name, greeting):
│ │ └── parameters (inputs)
│ └── function name
└── keyword
Return Values
# Single return value
def square(n):
return n ** 2
# Multiple return values (as tuple)
def min_max(numbers):
return min(numbers), max(numbers) # returns a tuple
lo, hi = min_max([3, 1, 4, 1, 5, 9]) # unpack
print(lo, hi) # 1 9
# No return (returns None implicitly)
def print_header(title):
print("=" * 40)
print(title.center(40))
print("=" * 40)
result = print_header("Report")
print(result) # None
# Early return
def find_first_negative(numbers):
for n in numbers:
if n < 0:
return n # exit immediately
return None # not found
Default Parameters
def connect(host, port=5432, timeout=30, ssl=True):
print(f"Connecting to {host}:{port} (ssl={ssl}, timeout={timeout}s)")
connect("localhost") # uses all defaults
connect("prod.server.com", port=443) # override port
connect("db.server.com", 3306, ssl=False) # positional then keyword
⚠️ Mutable default argument trap — one of the most common Python bugs:
# ❌ WRONG — the list is created ONCE and shared
def add_item(item, lst=[]):
lst.append(item)
return lst
add_item("a") # ['a']
add_item("b") # ['a', 'b'] — bug! shared list!
# ✅ CORRECT — use None as sentinel
def add_item(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
Keyword Arguments
def create_user(name, age, email, role="user", active=True):
return {"name": name, "age": age, "email": email,
"role": role, "active": active}
# Using keyword arguments (order doesn't matter)
user = create_user(
name="Alice",
age=30,
email="alice@example.com",
role="admin"
)
Type Hints (Python 3.5+)
Type hints document expected types — they don't enforce them at runtime but enable IDE checking.
def add(a: int, b: int) -> int:
return a + b
def process(items: list[str], limit: int = 10) -> list[str]:
return items[:limit]
# Optional (can be None)
from typing import Optional
def find_user(user_id: int) -> Optional[dict]:
...
# Union (multiple possible types)
from typing import Union
def format_value(value: Union[int, float, str]) -> str:
return str(value)
# Python 3.10+ shorthand
def find_user(user_id: int) -> dict | None:
...
Docstrings
Every function should have a docstring explaining what it does:
def calculate_bmi(weight_kg: float, height_m: float) -> float:
"""Calculate Body Mass Index (BMI).
Args:
weight_kg: Weight in kilograms.
height_m: Height in meters.
Returns:
The BMI value (weight / height²).
Raises:
ValueError: If height or weight is not positive.
Example:
>>> calculate_bmi(70, 1.75)
22.857142857142858
"""
if weight_kg <= 0 or height_m <= 0:
raise ValueError("Weight and height must be positive")
return weight_kg / (height_m ** 2)
Scope: LEGB Rule
Python resolves variable names using the LEGB rule:
L — Local (inside the function)
E — Enclosing (in outer function, for nested functions)
G — Global (module level)
B — Built-in (Python built-ins: print, len, range...)
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # "local" — L wins
inner()
print(x) # "enclosing"
outer()
print(x) # "global"
global and nonlocal
counter = 0
def increment():
global counter # modify global variable
counter += 1
# nonlocal — modify enclosing scope
def make_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
Functions are First-Class Objects
In Python, functions are objects — they can be stored in variables, passed as arguments, and returned from functions.
def double(x):
return x * 2
# Store in variable
operation = double
operation(5) # 10
# Pass as argument
numbers = [1, 2, 3, 4, 5]
doubled = list(map(double, numbers)) # [2, 4, 6, 8, 10]
# Store in data structures
operations = {
"double": double,
"triple": lambda x: x * 3
}
operations["double"](5) # 10
Key Vocabulary
| Term | Definition |
|---|---|
| Parameter | Variable in the function definition: def f(param) |
| Argument | Value passed when calling a function: f(value) |
| Return value | The result the function produces via return |
| Default parameter | Parameter with a preset value: def f(x=10) |
| Keyword argument | Argument specified by name: f(x=5) |
| Type hint | Optional annotation for parameter/return types |
| Docstring | String literal as first statement of a function |
| LEGB rule | Scope lookup order: Local → Enclosing → Global → Built-in |
Summary
- Functions are defined with
def, parameters in parentheses, and indented body returnsends a value back; without it, functions returnNone- Default parameters allow optional arguments — never use mutable defaults
- Keyword arguments
f(param=value)improve call-site readability - Type hints
name: strdocument expected types for tools and developers - Docstrings explain purpose, arguments, return value, and examples
- Functions are first-class objects — assignable, passable, and returnable
📄️ 04.1 - Function Basics
Define reusable, documented functions with parameters, return values, default arguments, and type hints
📄️ 04.2 - *args & **kwargs
Write flexible functions with variable arguments, keyword arguments, and capture state with closures
📄️ 04.3 - Lambda & Functional
Write concise anonymous functions and apply functional programming patterns with Python's built-in tools
📄️ 04.4 - Decorators
Understand and write Python decorators to add cross-cutting concerns like logging, timing, caching, and validation
📄️ Lab - Module 04
Build a data processing pipeline using functions, lambdas, map/filter/reduce, closures, and decorators
📄️ Quiz - Module 04
30 questions on function basics, *args/**kwargs, lambda, map/filter/reduce, closures, and decorators