Functions & modules
A function packages logic under a name so you can run it many times with different inputs. Functions turn a long script into small, testable pieces — the difference between code you write once and code a team can maintain.
Defining and calling
def gst_total(amount: float, rate: float = 18.0) -> float:
"""Return amount including GST."""
return round(amount * (1 + rate / 100), 2)
print(gst_total(1000)) # 1180.0
print(gst_total(1000, 5)) # 1050.0
print(gst_total(rate=12, amount=2000)) # 2240.0 - keyword arguments, any order
amount and rate are parameters; the values you pass are arguments. rate: float = 18.0 supplies a default,
so the caller may omit it. The type hints (: float, -> float) document intent for readers and editors; Python does
not enforce them at runtime. The text between triple quotes is the docstring, shown by help(gst_total).
Returning values
A function without return gives back None. You can return several values as a tuple and unpack them:
def split_bill(total, people):
share = total // people
remainder = total % people
return share, remainder
each, extra = split_bill(2575, 4)
print(each, extra) # 643 3
args and *kwargs
def summary(*numbers, **options):
total = sum(numbers)
return f"{options.get('label', 'Total')}: {total}"
print(summary(1, 2, 3, label="Sum")) # Sum: 6
*numbers collects extra positional arguments into a tuple; **options collects named ones into a dict.
Scope
Names created inside a function are local and disappear when it returns. A function can read a module-level name, but
assigning to one creates a new local name unless you declare global. Prefer passing values in and returning results
out — that keeps functions predictable and easy to test.
Modules
Split code into files and import them:
# utils.py
GST_RATE = 18
def slugify(text):
return text.lower().replace(" ", "-")
# main.py
from utils import slugify, GST_RATE
import utils # then call utils.slugify(...)
print(slugify("Kaju Katli Special")) # kaju-katli-special
Add a guard so a file can be both imported and run:
if __name__ == "__main__":
print(slugify("test run"))
The guarded code runs only when you execute python utils.py directly, never when another file imports it.
The standard library and pip
| Module | Use |
|---|---|
datetime |
dates, times and differences |
json / csv |
read and write data files |
pathlib |
file and folder paths |
random |
sampling and shuffling |
statistics |
mean, median, stdev |
math |
sqrt, ceil, floor, pi |
Install third-party packages with pip install package-name inside a virtual environment created by
python -m venv .venv, then record them with pip freeze > requirements.txt so a colleague can reproduce your setup.
Common mistakes
- Forgetting to
return, so the caller silently receivesNone. - Using a mutable default such as
def add(x, items=[]); useNoneand create the list inside. - Naming your file
json.pyorrandom.py, which shadows the standard library module. - Writing one 200-line function instead of four short ones; if you cannot name it in a few words, split it.
Practice
Create billing.py with gst_total(amount, rate=18), apply_discount(amount, percent) and
invoice_line(item, qty, price) that returns a formatted string. Import all three into main.py, print three
invoice lines for a textile order, and add an if __name__ == "__main__" self-test inside billing.py.