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
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
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 timesjson- Reading and writing JSON datacollections- Advanced data structures (Counter, defaultdict)osandpathlib- 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
importorfrom ... import ... - Use
if __name__ == "__main__"for script/module duality - Create your own modules for organization
Packages
- Folder with
__init__.pyfile - Use dot notation for importing
__init__.pycan 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