← Home

073. Importing Modules

Reuse existing libraries instead of reinventing the wheel

073. Importing Modules

He wants to display RAM usage as a percentage bar and compute log-scaled sizes for the process chart.

His first instinct: hand-roll everything.

PI = 3.14159   # close, but 6 digits of precision — not good enough

A classmate imports math instead:

import math

# math.pi → 3.141592653589793 (full float64)
# math.log2(rss_mb) — log scale for wildly different process sizes
# math.ceil(percent) — round UP to nearest whole percent

def scale_rss(rss_mb):
    return math.log2(rss_mb) if rss_mb > 0 else 0

def bar_width(percent, max_width=40):
    return math.ceil(percent / 100 * max_width)

The rule: before writing any math utility, check import math. math.sqrt, math.log, math.ceil, math.floor, math.gcd, math.pi — all free, all tested.


💡 Fun fact: Python’s module system caches every imported module in sys.modules after the first import. This means import math in 100 different files across your program costs only one actual load — subsequent imports are essentially free dictionary lookups. This design is why Python can have thousands of small modules without startup overhead.

⚠️ Watch out: A very common mistake is naming your own file the same as a standard library module — e.g., saving your code as math.py or random.py. Python searches the current directory first, so import math would import your file instead of the standard library, causing mysterious AttributeError: module 'math' has no attribute 'sqrt' errors.

🤔 Think about it: math.pi gives you 3.141592653589793 — 16 significant digits. Is that enough precision for all real-world calculations? What kind of computation would require even more precision, and how would Python handle it?

Learning objectives

  • Import modules using the import statement
  • Access module attributes with dot notation
  • Use math module functions and constants

Key concepts

  • import
  • module
  • standard library
  • math module

Try it

Concept detail

“import math” loads the math module and makes all its names available as math.X.

import math
math.pi          → 3.141592653589793   (full float64 precision)
math.sqrt(9)     → 3.0
math.log2(1024)  → 10.0
math.ceil(4.1)   → 5
math.floor(4.9)  → 4
math.gcd(12, 8)  → 4

WHY use the standard library instead of reimplementing:

  • math.pi has more digits than you can type correctly (3.14159 is off by 8e-6)
  • math.sqrt is implemented in C — faster than x**0.5
  • math.ceil handles edge cases (negative floats, large integers)
  • Code reads like intent: math.ceil says “ceiling function”, not “(int(x) + …)”

Python’s import system:

  • “import math” executes the module once, then caches it in sys.modules
  • Re-importing (import math a second time) is free — returns the cached object
  • Module = a .py file or C extension containing functions, classes, variables

Before writing any utility function, check docs.python.org/3/library — Python’s standard library has 200+ modules and it is probably already there.

Solution

import math

def circle_area(radius):
    return math.pi * radius ** 2

def circle_circumference(radius):
    return 2 * math.pi * radius

def hypotenuse(a, b):
    return math.sqrt(a**2 + b**2)

def log2_rss(rss_mb):
    return math.log2(rss_mb)

Tests

def test_circle_area_precision():
    import math
    # PI=3.14159 is off by ~8e-6; math.pi is accurate to full float precision
    assert abs(circle_area(1) - math.pi) < 1e-10

def test_circle_area_5():
    import math
    assert abs(circle_area(5) - math.pi * 25) < 1e-10

def test_circle_circumference():
    import math
    assert abs(circle_circumference(1) - 2 * math.pi) < 1e-10

def test_hypotenuse_345():
    assert abs(hypotenuse(3, 4) - 5.0) < 0.0001

def test_hypotenuse_512():
    assert abs(hypotenuse(5, 12) - 13.0) < 0.0001

def test_log2_rss():
    assert abs(log2_rss(1024) - 10.0) < 0.0001
    assert abs(log2_rss(1) - 0.0) < 0.0001

Resources