Modules and Packages
Organizing and reusing Python code.
Modules
A module is a single .py file:
# mymodule.py
"""Module docstring - describes what this module does."""
PI = 3.14159
def greet(name):
return f"Hello, {name}!"
class Calculator:
def add(self, a, b):
return a + b
Importing Modules
# Import entire module
import mymodule
mymodule.greet("Alice")
mymodule.PI
# Import with alias
import mymodule as mm
mm.greet("Alice")
# Import specific items
from mymodule import greet, PI
greet("Alice")
# Import with alias
from mymodule import greet as say_hello
say_hello("Alice")
# Import all (avoid - pollutes namespace)
from mymodule import *
Module Search Path
Python looks for modules in this order:
- Current directory
PYTHONPATHenvironment variable- Standard library
- Site-packages (installed packages)
import sys
print(sys.path) # List of search directories
The name Variable
# mymodule.py
def main():
print("Running as script")
if __name__ == "__main__":
# Only runs when executed directly, not when imported
main()
python mymodule.py # Runs main()
import mymodule # Does NOT run main()
Packages
A package is a directory containing modules:
mypackage/
├── __init__.py # Makes it a package
├── module1.py
├── module2.py
└── subpackage/
├── __init__.py
└── module3.py
init.py
Runs when package is imported. Use it to:
# mypackage/__init__.py
# Control what's exported with "from package import *"
__all__ = ['module1', 'useful_function']
# Import commonly used items for convenience
from .module1 import useful_function
from .module2 import UsefulClass
# Package-level constants
VERSION = "1.0.0"
Importing from Packages
# Import module from package
import mypackage.module1
mypackage.module1.function()
# Import specific item
from mypackage.module1 import function
function()
# Import from subpackage
from mypackage.subpackage import module3
# Import package (uses __init__.py exports)
import mypackage
mypackage.useful_function()
Relative Imports
Within a package, use relative imports:
# In mypackage/module2.py
from . import module1 # Same package
from .module1 import function # Specific item
from ..otherpackage import x # Parent's sibling
Relative imports only work within packages, not in scripts run directly.
Virtual Environments
Isolated Python environments for project dependencies:
Creating and Using
# Create virtual environment
python3 -m venv venv
# Activate (Linux/macOS)
source venv/bin/activate
# Activate (Windows)
venv\Scripts\activate
# Deactivate
deactivate
Managing Dependencies
# Install package
pip install requests
# Install specific version
pip install requests==2.28.0
# Install from requirements file
pip install -r requirements.txt
# Export current packages
pip freeze > requirements.txt
# Show installed packages
pip list
# Show package info
pip show requests
# Upgrade package
pip install --upgrade requests
# Uninstall
pip uninstall requests
requirements.txt
requests==2.28.0
flask>=2.0,<3.0
numpy
pytest>=7.0
pyproject.toml (Modern)
[project]
name = "myproject"
version = "1.0.0"
dependencies = [
"requests>=2.28",
"flask>=2.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"black",
]
Installing Packages
pip
# From PyPI
pip install package_name
# From GitHub
pip install git+https://github.com/user/repo.git
# From local directory
pip install /path/to/package
# Editable install (for development)
pip install -e .
Common Packages
| Category | Packages |
|---|---|
| Web | requests, httpx, flask, fastapi, django |
| Data | pandas, numpy, polars |
| Testing | pytest, unittest |
| CLI | click, typer, argparse |
| Config | python-dotenv, pyyaml, toml |
| Database | sqlalchemy, psycopg2, sqlite3 |
| Async | asyncio, aiohttp, trio |
Standard Library Highlights
Built-in modules - no installation needed:
# Operating system
import os
import sys
import shutil
# File paths
from pathlib import Path
# Data formats
import json
import csv
import configparser
# Date and time
import datetime
import time
# Math
import math
import random
import statistics
# Text
import re
import string
import textwrap
# Data structures
from collections import defaultdict, Counter, deque
from itertools import chain, groupby, combinations
from functools import reduce, partial, lru_cache
# Concurrency
import threading
import multiprocessing
import asyncio
import concurrent.futures
# Networking
import urllib.request
import socket
import http.server
# Testing
import unittest
import doctest
# Debugging
import pdb
import logging
import traceback
# Compression
import gzip
import zipfile
import tarfile
Creating Your Own Package
Basic Structure
myproject/
├── pyproject.toml
├── README.md
├── src/
│ └── mypackage/
│ ├── __init__.py
│ ├── core.py
│ └── utils.py
└── tests/
├── __init__.py
└── test_core.py
pyproject.toml
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "mypackage"
version = "0.1.0"
description = "A short description"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"requests>=2.28",
]
[project.scripts]
mycommand = "mypackage.cli:main"
Installing Locally
# Install in development mode
pip install -e .
# Now you can import it anywhere
python -c "import mypackage"
Import Best Practices
Import Order
# 1. Standard library
import os
import sys
from pathlib import Path
# 2. Third-party packages
import requests
import pandas as pd
# 3. Local packages
from mypackage import utils
from .module import function
What to Avoid
# Avoid: importing inside functions (usually)
def process():
import pandas # Slow, hard to track dependencies
# Avoid: circular imports
# file_a.py imports file_b.py which imports file_a.py
# Avoid: import *
from module import * # Pollutes namespace, unclear dependencies
# Avoid: shadowing standard library
# Don't name your file: email.py, random.py, etc.
Lazy Imports
For expensive imports in CLI tools:
def expensive_operation():
import pandas as pd # Only import when needed
return pd.DataFrame()
Namespace Packages (Advanced)
Packages without __init__.py - can be split across directories:
# Two separate directories, same namespace
dir1/mynamespace/package_a/
dir2/mynamespace/package_b/
# Both accessible as:
from mynamespace import package_a
from mynamespace import package_b
Practice
# 1. Create a simple package structure
"""
calculator/
├── __init__.py
├── basic.py # add, subtract, multiply, divide
└── advanced.py # power, sqrt, factorial
"""
# calculator/__init__.py
from .basic import add, subtract
from .advanced import power
# calculator/basic.py
def add(a, b):
return a + b
def subtract(a, b):
return a - b
# Usage:
from calculator import add
add(2, 3)
# 2. Virtual environment workflow
"""
python3 -m venv venv
source venv/bin/activate
pip install requests
pip freeze > requirements.txt
"""
# 3. Check if module exists
import importlib.util
def module_exists(name):
return importlib.util.find_spec(name) is not None
module_exists("requests") # True or False