Modules & Packages

Organizing your code and using external functionality.

Introduction

As your programs grow, keeping all your code in a single file becomes unmanageable. Python lets you break code into modules (reusable files) that can be imported and used in other programs. In this lesson, you'll learn how to import standard modules, create your own, and utilize packages.

What is a Module?

A module is any Python file (ending in .py) that can contain variables, functions, classes, and runnable code. Modules help you organize code by grouping related objects and functions together.

# greetings.py
def say_hello(name):
    print(f"Hello, {name}!")

Importing Modules

Use the import statement to use code from other modules, both standard library and your own files.

# Importing a module
import math
print(math.sqrt(16))

# Importing specific items
from math import pi, sin
print(pi)
print(sin(0))

# Importing your own module: greetings.py from above sits next to this file
import greetings
greetings.say_hello("Alice")
Expected Output:
4.0
3.141592653589793
0.0
Hello, Alice!

Aliasing & Import Variants

# Use 'as' to create an alias
import math as m
print(m.sqrt(25))

# The same idea applies to third-party packages, once they are installed:
# import numpy as np
# import pandas as pd

# Wildcard (not recommended)
from math import *

# sqrt() and pi are now available without the math. prefix
print(sqrt(49))
print(pi)
Expected Output:
5.0
7.0
3.141592653589793
Warning: Avoid from module import * in real projects; it pollutes your namespace and makes code harder to understand. But: There are niche cases, like plugin or component registration patterns, where wildcard imports can be useful for auto-discovering components.

Note: Third-party packages like numpy and pandas must be installed first using pip install numpy pandas before you can import them. Standard library modules (like math, random) work out of the box.

Creating Your Own Modules

# In file: calculator.py
def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

# In another file, sitting next to calculator.py:
import calculator

print(calculator.add(3, 4))
print(calculator.subtract(10, 5))
Expected Output:
7
5
Tip: Place your reusable code (utility functions, constants) into modules to keep your main scripts clean and organized.

Packages (Directories of Modules)

A package is a folder containing an __init__.py file and any number of modules. Import packages using dot notation. (Since Python 3.3, a folder without __init__.py can also be imported as a namespace package, but including the file is still the norm for regular packages: it is what lets you run package initialization code.)

Folder Structure

Here is the package we will build. Two things make it work: the mypackage/ folder holds the modules, and __init__.py marks that folder as a package rather than an ordinary directory. The script that uses it, main.py, sits beside the folder, not inside it, which is what lets import mypackage find it.

project/
├── main.py
└── mypackage/
    ├── __init__.py
    ├── helpers.py
    └── math_utils.py

The dotted path in an import mirrors this tree one segment at a time: mypackage.math_utils means the math_utils module inside the mypackage folder.

Create the whole thing from an empty directory:

# Terminal
mkdir project
cd project

mkdir mypackage
touch main.py
touch mypackage/__init__.py mypackage/helpers.py mypackage/math_utils.py

That leaves you with five empty files. The next sections fill in three of them, one at a time.

The helpers Module

Two small string utilities. Nothing about this file is special: it is an ordinary module that happens to live inside a package folder.

# mypackage/helpers.py
def format_name(name):
    return name.strip().title()

def clean_text(text):
    return " ".join(text.split())

The math_utils Module

A second module in the same package. Grouping related modules under one folder is the entire point of packages: helpers and math_utils stay separate files, but ship and import as one unit.

# mypackage/math_utils.py
def square(n):
    return n * n

def cube(n):
    return n * n * n

Using the Package

Now main.py can reach both modules. The two import forms below are worth contrasting: the first pulls in the whole helpers module, so its functions stay behind the helpers. prefix; the second reaches into math_utils and pulls out a single name, so square() is used bare.

# main.py
from mypackage import helpers
from mypackage.math_utils import square

print(helpers.format_name("  ada lovelace  "))
print(helpers.clean_text("too    many   spaces"))
print(square(7))
Expected Output:
Ada Lovelace
too many spaces
49

What Goes in __init__.py?

The __init__.py file can be empty (it just marks the directory as a package), or it can contain initialization code and convenient imports.

# mypackage/__init__.py
"""
This file makes the directory a Python package.
Can be empty, or can contain initialization code.
"""

# Import commonly used items for convenience
from .helpers import format_name, clean_text
from .math_utils import square, cube

# Package-level variables
__version__ = "1.0.0"
__author__ = "Your Name"

Those re-exports change how callers reach the same functions. Instead of naming the module inside the package (from mypackage.math_utils import square), they can import straight from the package:

# main.py
import mypackage
from mypackage import square, format_name

print(square(9))
print(format_name("grace hopper"))
print(mypackage.__version__)
Expected Output:
81
Grace Hopper
1.0.0

Best Practice: Use __init__.py to expose commonly used functions at the package level. This makes your package easier to use and hides internal organization from users.

The if __name__ == "__main__" Pattern

This special pattern allows you to write modules that can be both imported (as a library) and executed (as a script). It's one of the most important patterns in Python module design.

How It Works

# calculator.py
def add(a, b):
    """Add two numbers"""
    return a + b

def subtract(a, b):
    """Subtract b from a"""
    return a - b

def multiply(a, b):
    """Multiply two numbers"""
    return a * b

# This code only runs when script is executed directly
if __name__ == "__main__":
    # Test/demo code
    print("Testing calculator module...")
    print(f"5 + 3 = {add(5, 3)}")
    print(f"10 - 4 = {subtract(10, 4)}")
    print(f"7 * 6 = {multiply(7, 6)}")
    print("All tests passed!")
Expected Output:
Testing calculator module...
5 + 3 = 8
10 - 4 = 6
7 * 6 = 42
All tests passed!

That output is what python calculator.py prints. Running the file directly sets__name__ to "__main__", so the test code executes. Importing it (import calculator) sets __name__ to "calculator" instead, so the same block is skipped and nothing is printed: the caller gets the three functions and none of the demo noise.

Practical Example

# data_processor.py
def clean_data(data):
    """Remove empty strings and strip whitespace"""
    return [item.strip() for item in data if item.strip()]

def calculate_average(numbers):
    """Calculate average of numbers"""
    if not numbers:
        return 0
    return sum(numbers) / len(numbers)

if __name__ == "__main__":
    # Demo: shows how to use the module
    sample_data = ["  hello  ", "", "  world  ", "   "]
    cleaned = clean_data(sample_data)
    print(f"Cleaned data: {cleaned}")

    scores = [85, 92, 78, 95, 88]
    avg = calculate_average(scores)
    print(f"Average score: {avg}")
Expected Output:
Cleaned data: ['hello', 'world']
Average score: 87.6

When to use this pattern:

  • Adding test/demo code to modules
  • Creating command-line tools (scripts that can also be imported)
  • Running examples when module is executed directly
  • Debugging/development testing

Using the Standard Library

Python comes with a rich standard library - a collection of modules that are always available without installation. Here are some commonly used examples:

math

Mathematical functions and constants

import math

# Common functions
print(math.sqrt(36))
print(math.pi)
print(math.ceil(4.2))
print(math.floor(4.8))
Expected Output:
6.0
3.141592653589793
5
4
random

Random number generation and choices

import random

# seed() fixes the sequence, so this example prints the same
# values on every run. Leave it out to get fresh values.
random.seed(42)

# Random numbers
print(random.randint(1, 100))   # both ends included
print(random.random())          # 0.0 <= x < 1.0

# Random choice
colors = ["red", "blue", "green"]
print(random.choice(colors))
Expected Output:
82
0.11133106816568039
green

Want to learn more? The standard library includes many powerful modules for common tasks:

  • datetime - Working with dates and times
  • json - Reading and writing JSON data
  • collections - Advanced data structures (Counter, defaultdict)
  • os and pathlib - File system operations

These are covered in detail in Lesson 12: Standard Library Essentials.

Key Takeaways

Modules

  • Break code into reusable .py files
  • Import with import or from ... import ...
  • Use if __name__ == "__main__" for script/module duality
  • Create your own modules for organization

Packages

  • Folder with __init__.py file
  • Use dot notation for importing
  • __init__.py can expose convenient imports
  • Group related modules together

Third-Party Packages

  • Use pip install package_name
  • Standard library modules work out of the box
  • Popular packages: numpy, pandas, requests

Best Practices

  • Use aliases for long names (import numpy as np)
  • Avoid from module import *
  • See Lesson 12 for standard library deep dive
What's Next?

You're now ready to organize your code and use powerful external libraries! In the next lessons, we'll explore:

  • String manipulation & formatting - Master string operations, slicing, and modern formatting techniques
  • List comprehensions & lambda - Write cleaner, more Pythonic code with elegant one-liners
  • Standard library essentials - Leverage datetime, json, collections, and os modules
  • Debugging techniques - Learn to find and fix bugs like a professional developer