Python
Beginner 12 min read Python

Functions and Modules

Structure that scales with the dataset

Functions package logic into testable units; modules and packages organize them into libraries. Returning early, keyword args and docstrings separate clean data code from notebook spaghetti.

functiondefargskwargsmoduleimportside effects
Before we start

What you will learn

  • Write functions with clear inputs, outputs and defaults
  • Explain why functions without side effects are safer
  • Import and organize code into modules
Introduction

What is this?

Functions are named, reusable blocks of logic: `def clean(df): ... return df`. Modules are `.py` files holding related functions; packages are folders of modules. Data science code that stays in notebooks turns into untestable scripts as it grows; extracting pure, documented functions into modules is what makes analyses maintainable and reusable.

Motivation

Why do you need it?

  • Notebook linear flow becomes unmaintainable past a few hundred cells.
  • Pure functions (same input → same output, no global mutation) are testable and cacheable.
  • Well-named modules let teams share transformations instead of re-implementing them.
  • Functions are where unit tests, docstrings and type hints live.
Think about it

Mental model

Imagine

A chef's mise en place

Think of

Prepared functions before the rush

Gather the ingredients and compose recipes before service. Clean, small, tested functions prepared in advance make the final pipeline (the meal service) fast and repeatable.

Mechanics

How it works

  • def name(params, default=...): body; return ...
  • Positional vs keyword args: callers can pass f(x, y=2) to be explicit.
  • A function with no side effects only maps inputs to outputs — safe to call anywhere, any time.
  • import module runs the module once and exposes names; `from x import y` imports specific names.
  • if __name__ == '__main__': guards module-level code so imports don't execute it.

From notebook to modular code.

Notebook cells
Extract functions
Move to modules
Import + call
Notebook = thin shell
Hands-on

Examples

A pure, documented transformer

A pure, documented transformer
def flag_outliers(s, k=3):
"""Return a boolean Series marking values > k MAD from the median."""
med = s.median()
mad = (s - med).abs().median()
return (s - med).abs() > k * (1.4826 * mad)
import pandas as pd
col = pd.Series([1, 2, 3, 2, 100])
print(flag_outliers(col).tolist()) # [False, False, False, False, True]

Pure function: same input → same output, no globals mutated, documented parameters. The pandas median/MAD logic inside is robust to the outlier that would corrupt a mean/std approach.

Real world

In production

  • A team shares one features.py with curated transformers, imported by all notebooks and the serving API.
  • CI runs pytest against each pipeline function before data refresh deploys.
  • A modeling group wraps training in train(params) so experiments differ only in parameters.
Guidelines

Best practices

  • Make functions pure where possible; isolate and document mutations.
  • Give defaults for tunable constants (k=3) and document what they mean.
  • Write one docstring line on purpose and parameters.
  • Keep functions small enough to read top-to-bottom.
Watch out

Common mistakes

  • Mutating an input DataFrame in place without a copy, surprising the caller.
  • Module-level import-time heavy work (loading data at import).
  • Massive 200-line functions with hidden global state.
When things break

Debugging tips

  • Test each function in isolation with a tiny fixture before composing the pipeline.
  • Name functions by action (clean_, compute_, flag_) for greppable codebases.
  • If two callers get different results, suspect shared mutable state inside the function.
Interviews

Interview checkpoint

Test yourself

Quick quiz

0/2
1

A function that only maps its inputs to outputs, with no external effects, is called:

2

Functional mutations of the input argue for a local .copy() inside the function.

Go deeper

Official documentation