Write a reusable Python module from a notebook
In this article (6 sections)
Extract the stable calculation into functions with explicit inputs and outputs, then let the notebook call those functions. Keep exploratory charts and narrative in the notebook while moving repeatable parsing, validation and metric logic into an importable module.
The hardest part is usually removing hidden state: variables created in an earlier cell, manually filtered dataframes and files that happen to exist on the author's laptop.
Identify the calculation boundary
The Python reporting lab separates parse_order, load_orders, paid_summary, run and main. These names reflect different responsibilities rather than arbitrary cell boundaries.
paid_summary receives validated orders and a month. It does not read a global dataframe, open a file or infer the reporting period from today's date. That makes it suitable for a notebook, a command-line program or a test.
from datetime import date
from decimal import Decimal
from report import Order, paid_summary
orders = [
Order("A", "0012", date(2026, 1, 31), Decimal("19.00"), "Paid"),
Order("B", "0042", date(2026, 2, 1), Decimal("10.00"), "Paid"),
Order("C", "0012", date(2026, 1, 31), Decimal("8.00"), "Pending"),
]
result = paid_summary(orders, "2026-01")
assert result == {"month": "2026-01", "currency": "INR", "paid_orders": 1,
"known_customers": 1, "paid_amount_inr": "19.00"}
assert len(orders) == 3
print(result)The small example exposes the month boundary and status rule. It also confirms that the input list remains present rather than being destructively filtered.
Make imports quiet
Importing a calculation module should not automatically run last month's report. The supplied report.py places command-line execution under if __name__ == "__main__", so importing Order or paid_summary defines functionality without invoking the CLI.
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "import report"],
capture_output=True, text=True, check=False)
assert result.returncode == 0
assert result.stdout == "" and result.stderr == ""
print("Import does not execute the report CLI")This checks the observable import behavior of the current module. It is not a general proof that every imported dependency is free of side effects. Python's modules tutorial explains module execution and the main-module convention.
Replace notebook globals with arguments
If a function uses a variable called cleaned_df that exists only in the notebook, pass the required data explicitly. If it uses start_date from a previous cell, make the period part of the function signature.
Avoid default arguments that capture mutable objects such as lists intended to be fresh on every call. Also avoid deriving a reporting cutoff from the current clock unless that behavior is part of the contract and can be controlled in tests.
The goal is not to eliminate all state from an application. It is to make the state that affects the result visible at the calculation boundary.
Keep the notebook as an explanation
A good notebook can load the fixture, call the module, display reconciliation tables and explain unexpected records. Restarting the kernel and running all cells should reproduce the result in order.
Do not maintain two separate implementations, one in the notebook and one in the module. They will drift. The notebook should import the tested function and focus on interpretation or exploration around its output.
During development, remember that an already imported module can remain cached in a notebook session. Restarting the kernel provides a straightforward clean check after edits. A stale in-memory function can otherwise make a saved file appear to behave differently.
Review the extraction through behavior
Before refactoring, capture a small expected result. After extraction, run the same input through the module and compare outputs, including rejected records and boundary cases. Passing only the headline total can miss a changed population.
Exercise: create a notebook with one input-loading cell, one module call and one result explanation. Restart and run all cells, then run the equivalent CLI command and reconcile the business values.
NeuraPath's Data Analytics with Generative AI course connects exploratory analysis with reusable Python. A strong submission lets another analyst inspect both the explanation and the calculation that produced it.
Continue learning
This article is part of the Python foundations for analysts sequence. Use the neighbouring tasks when you need the prerequisite or the next application.
- Review the prerequisite or neighbouring task in Use context managers to close files reliably.
- Continue with Test a business calculation with normal and boundary cases.
Pankit Kumar has 10 years in Data Science & AI, building and shipping production systems in regulated pharma and clinical environments. He is a freelance trainer at Boston Institute of Analytics, AnalytixLabs and Scaler, and has taught this material to thousands of working professionals.
This article is part of our Data Analytics with Generative AI programme — 3–4 months. The full analyst stack — Excel, SQL, Power BI and Python pipelines — then a generative-AI layer you can prove is right.
Explore Data Analytics with Generative AI