Modules & Packages

Master Python imports, pip, virtual environments, package structure, and essential standard library modules.

Intermediate 30 min read 🐍 Python

The Import System

As your programs grow beyond a single file, you need a way to organize code into reusable pieces. Python's module system lets you split code into separate files and import functionality from them. A module is simply a .py file. A package is a directory containing modules.

Python has three types of imports: the standard library (built-in modules like os, json, datetime), third-party packages (installed via pip, like requests, flask), and your own modules (your .py files).

Import Styles

Python offers several ways to import. Each has its place:

# Import the entire module
import math
print(math.pi)        # 3.141592653589793
print(math.sqrt(16))  # 4.0

# Import specific items
from datetime import datetime, timedelta
now = datetime.now()
tomorrow = now + timedelta(days=1)
print(f"Now: {now:%Y-%m-%d}")
print(f"Tomorrow: {tomorrow:%Y-%m-%d}")

# Import with alias (common for long names)
import numpy as np
import pandas as pd

# Import everything (avoid this — pollutes namespace)
# from math import *  # Bad practice!
Output
3.141592653589793
4.0
Now: 2024-01-15
Tomorrow: 2024-01-16

Import Best Practices

Use import module for standard library modules (makes it clear where functions come from). Use from module import specific_thing when you use something frequently. Avoid from module import * — it makes code hard to read because you can't tell where names came from.

Creating Your Own Modules

Any Python file is a module. If you have mymath.py, you can import from it in another file:

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

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

PI = 3.14159

# This block only runs when the file is executed directly,
# NOT when it's imported as a module
if __name__ == '__main__':
    print(f"Testing: add(2, 3) = {add(2, 3)}")
    print(f"Testing: multiply(4, 5) = {multiply(4, 5)}")
# main.py — using our module
from mymath import add, PI

result = add(10, 20)
print(f"{result}, PI = {PI}")
Output
30, PI = 3.14159
Key Takeaway: The if __name__ == '__main__': guard is essential. It lets your module file work both as a standalone script AND as an importable library. Without it, test code would run every time someone imports your module.

Package Structure

A package is a directory that contains Python modules and a special __init__.py file (which can be empty). Packages let you organize related modules into a hierarchy:

myproject/
    __init__.py          # Makes this a package (can be empty)
    core.py              # Main functionality
    utils.py             # Helper functions
    models/
        __init__.py      # Sub-package
        user.py
        product.py
# Importing from packages
from myproject.core import main_function
from myproject.models.user import User
from myproject.utils import format_date

pip and Virtual Environments

The Python Package Index (PyPI) hosts over 400,000 third-party packages. pip is the tool to install them. But there's a catch: if you install packages globally, different projects might need different versions of the same package. This is where virtual environments come in.

A virtual environment is an isolated Python installation for your project. Each project gets its own set of packages, avoiding version conflicts:

# Create a virtual environment
python3 -m venv myproject_env

# Activate it
source myproject_env/bin/activate   # macOS/Linux
myproject_env\Scripts\activate      # Windows

# Your prompt changes to show the active environment:
# (myproject_env) $

# Install packages (goes into this environment only)
pip install requests flask pandas

# See what's installed
pip list

# Save dependencies to a file
pip freeze > requirements.txt

# Someone else can recreate your environment:
pip install -r requirements.txt

# Deactivate when done
deactivate

⚠️ Common Mistake: Installing Packages Globally

Wrong:

pip install requests  # Installs globally — affects ALL projects!

Why: Global installs cause version conflicts between projects. Project A needs requests==2.28 but Project B needs requests==2.31 — impossible with global installs.

Instead:

python3 -m venv venv
source venv/bin/activate
pip install requests  # Isolated to this project

Essential Standard Library Modules

Python's standard library is famously comprehensive — "batteries included." Here are the modules you'll use most often:

# collections — specialized containers
from collections import Counter, defaultdict, namedtuple

words = "the cat sat on the mat the cat".split()
print(Counter(words))  # Count occurrences
# Counter({'the': 3, 'cat': 2, 'sat': 1, 'on': 1, 'mat': 1})

# defaultdict — dict with default values for missing keys
scores = defaultdict(list)
scores["Alice"].append(95)
scores["Alice"].append(87)
scores["Bob"].append(92)
print(dict(scores))
# {'Alice': [95, 87], 'Bob': [92]}
Output
Counter({'the': 3, 'cat': 2, 'sat': 1, 'on': 1, 'mat': 1})
{'Alice': [95, 87], 'Bob': [92]}
# functools — higher-order function utilities
from functools import lru_cache, reduce

@lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(50))  # Instant with caching!
# 12586269025

# os and pathlib — file system operations
from pathlib import Path
import os

print(os.getcwd())           # Current directory
print(Path.home())           # Home directory
print(list(Path(".").glob("*.py")))  # All .py files
ModulePurposeKey Functions
osOperating system interfacegetcwd(), listdir(), environ
sysSystem-specific parametersargv, path, exit()
pathlibObject-oriented pathsPath(), .glob(), .read_text()
jsonJSON encoding/decodingdumps(), loads(), dump(), load()
datetimeDate and timedatetime.now(), timedelta
collectionsSpecialized containersCounter, defaultdict, namedtuple
functoolsFunction toolslru_cache, partial, wraps
itertoolsIterator utilitieschain, combinations, groupby
reRegular expressionssearch(), findall(), sub()
loggingLogging frameworkgetLogger(), basicConfig()
🔍 Deep Dive: How Python Finds Modules

When you write import mymodule, Python searches these locations in order: (1) the current directory, (2) directories in the PYTHONPATH environment variable, (3) the standard library directory, (4) the site-packages directory (where pip installs). You can see the full search path with import sys; print(sys.path). If Python can't find the module in any of these locations, you get an ImportError.