Modules: math, random and friends
Part of the free LogicWiz Python course, Module 3: Functions. The examples and practice run in the app.
Every year the board paper shows random.randint(1, 3) inside a loop and asks which of four outputs is possible. Half the class loses those marks because they forget that randint(1, 3) can give 3 but randrange(1, 3) can't.
You'll learn to
- Import a module three ways: `import`, `from … import`, `as`
- Use `math`, `random`, `statistics` and `datetime`
- Answer "which outputs are possible?" questions with a method
- Know what `if __name__ == "__main__":` is for
Learn
A module is a file of ready-made functions
Python ships with hundreds of modules. Three ways to use one:
import math→ callmath.sqrt(16)from math import sqrt→ callsqrt(16)(onlysqrtcomes in, notmath)import statistics as st→ callst.mean(…)(a short nickname)
Avoid from math import *: it dumps every name into your file and you can't tell where pow came from.
math
import math
print(math.sqrt(49))
print(math.ceil(4.1), math.floor(4.9))
print(math.ceil(-4.1), math.floor(-4.9))
print(math.pow(2, 3), 2 ** 3)
print(round(math.pi, 4))
print(math.fabs(-7), abs(-7))
from math import gcd, factorial
print(gcd(12, 18), factorial(5))
ceil goes up to the next whole number, floor goes down, even for negatives (floor(-4.9) is -5). sqrt, pow and fabs always give a float.
random: what each call can give
| Call | Possible values |
|---|---|
| random.randint(1, 6) | 1, 2, 3, 4, 5, 6 (both ends included) |
| random.randrange(1, 6) | 1, 2, 3, 4, 5 (stop excluded, like range) |
| random.randrange(10, 50, 10) | 10, 20, 30, 40 |
| random.random() | a float from 0.0 up to, but not including, 1.0 |
| random.uniform(1, 5) | a float between 1 and 5 |
| random.choice("ABC") | 'A', 'B' or 'C' |
Seeding makes "random" repeatable
import random
random.seed(7)
print(random.randint(1, 6))
print(random.randrange(10, 20, 5))
print(random.choice(["samosa", "vada pav", "poha"]))
print(round(random.random(), 3))
Run it twice: same output, because seed(7) restarts the same sequence. Delete the seed line and the output changes every run. Seeds are for testing; a real game wouldn't use one.
The method for "which outputs are possible?"
- For each random call, write its exact range (does it include the end?).
- If the call is inside a loop, the range may change each time round (
randint(0, i)). - Check each option value by value; one impossible value kills the option.
The board also asks for the minimum and maximum a variable can take. Same step 1.
statistics and datetime
import statistics as st
marks = [72, 85, 85, 90, 64]
print(st.mean(marks), st.median(marks), st.mode(marks))
from datetime import date
exam = date(2027, 3, 2)
today = date(2026, 10, 24)
print((exam - today).days, "days to the boards")
print(exam.strftime("%d-%m-%Y"), exam.year)
[72, 85, …] is a list of values; you'll meet lists properly in Module 5. Subtracting two dates gives a gap whose .days you can print.
__name__: am I being run or imported?
Every file has a hidden name __name__. When you run the file, it is "__main__". When another file imports it, it is the file's own name (for bill.py, "bill").
So put demo or test code under if __name__ == "__main__":. It runs when you run the file, and stays quiet when someone imports your functions.
The main guard
def gst(amount):
return round(amount * 1.05, 2)
print("__name__ is", __name__)
if __name__ == "__main__":
print("Testing gst:", gst(200))
Never name your own file random.py, math.py or statistics.py. Then import random finds your file instead of Python's, and you get AttributeError: module 'random' has no attribute 'randint'.
In one line
import m → m.f(), from m import f → f(); randint(a, b) includes b, randrange(a, b) doesn't; guard demo code with if __name__ == "__main__":.