Python Functions
Tutorial 17 of 65 · pythondeck.com Python course
Functions are defined with def and can have positional, keyword, default, *args and **kwargs parameters. Returning multiple values returns a tuple. Functions are first-class objects and can be passed, stored and returned.
Functions package reusable logic with a name, parameters, and return value. They are first-class objects—you can pass them as arguments, store them in variables, and return them from other functions. The standard library is organized as functions and methods you compose, not monolithic programs.
Good functions have a single clear purpose, explicit parameters, and documented contracts—the foundation of testing and modular design. Most scripts are thin orchestration layers calling well-named functions rather than one long procedural block.
Definition: def name(params): body; return sends a value back (or None).
Parameters: positional, keyword, defaults, *args, **kwargs, keyword-only after *.
Docstrings: first statement string documents behavior for help().
Scope: local names inside functions; global/nonlocal for rebinding outer names.
Annotations: def f(x: int) -> str: for hints, not runtime enforcement.
Recursion is supported; watch base cases and stack depth.
Pure functions (no side effects) simplify testing. Impure functions coordinate I/O and state—keep them thin and push logic inward.
Closures capture variables from enclosing scopes—useful for factories and decorators.
Decorators wrap defs; wrappers forward *args and **kwargs to preserve signatures.
Mutable default arguments shared across calls.
Returning multiple values without documenting tuple order.
Functions that do too much—god functions resist testing.
Using mutable global state instead of passing parameters.
Keep parameter lists short; group related options in a dataclass or config object.
Use type hints and docstrings on public APIs.
Raise specific exceptions rather than returning error codes unless style guide says otherwise.
Name functions with verbs (parse_config, save_report).
Split functions that exceed one screen into helpers so each unit tests independently.
Re-read the examples below with these ideas in mind; change variable names and inputs to match your own project.
The program below demonstrates definition. Read the comments on each line, run the code, then change names or values to see how the output shifts.
# Example: Definition
# Run in the REPL or save as a .py file and execute with python.
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Ada"))
print(greet("Linus", greeting="Hi"))
This sample walks through *args / **kwargs in a small, runnable script. Paste it into the REPL or save it as a .py file before you continue to the next block.
# Example: *args / **kwargs
# Run in the REPL or save as a .py file and execute with python.
def report(*args, **kwargs):
print("args :", args)
print("kwargs:", kwargs)
report(1, 2, 3, user="ada", level=5)
Here is a hands-on illustration of higher order. Follow the inline comments first; only then execute the snippet and compare the result with what you expected.
# Example: Higher order
# Run in the REPL or save as a .py file and execute with python.
def apply(fn, x):
return fn(x)
print(apply(lambda v: v*v, 9))
The program below demonstrates args kwargs. Read the comments on each line, run the code, then change names or values to see how the output shifts.
# *args collects extra positional values as a tuple
def total(*numbers): # arbitrary positional count
return sum(numbers) # built-in sum over tuple
print(total(1, 2, 3)) # 6
def profile(name, **fields): # **kwargs -> dict
return {"name": name, **fields} # merge dicts
print(profile("Ada", role="dev", city="London")) # dict output
def both(a, b=0, *rest, flag=False, **meta): # full signature
return (a, b, rest, flag, meta) # echo parameters
print(both(1, 2, 3, 4, flag=True, x=9)) # inspect tuple parts
This sample walks through return multiple in a small, runnable script. Paste it into the REPL or save it as a .py file before you continue to the next block.
# Functions return one object; tuple packing returns many values
def minmax(values): # compute bounds of a sequence
return min(values), max(values) # tuple of two ints
lo, hi = minmax([4, 1, 9, 3]) # unpack at call site
print(lo, hi) # 1 9
def apply(fn, x): # higher-order helper
return fn(x) # call passed function
print(apply(lambda v: v * 2, 21)) # 42
def greet(name, excited=False): # default parameter
text = f"Hello, {name}" # base greeting
return text + "!!!" if excited else text # branch return
print(greet("Python")) # calm greeting
Continue with these focused follow-up lessons on Python Functions: