Modules, Packages & pip
After this lesson, you will be able to:
- Import modules and packages using import and from...import syntax
- Understand what __name__ == '__main__' means and why it exists
- Use pip to install third-party packages from PyPI
- Create virtual environments to isolate project dependencies
- Write a requirements.txt file to make projects reproducible
Before You Start
#What Is a Module?
.py file containing Python code. When you import it, you get access to its functions, classes, and variables.# Importing entire modules
import math
import random
import os
import datetime
# Using module functions with module_name.function_name
print(math.sqrt(16)) # 4.0
print(math.pi) # 3.141592653589793
print(random.randint(1, 6)) # random number 1-6 (like a dice roll)
print(os.getcwd()) # current working directory
# Getting today's date
today = datetime.date.today()
print(f"Today: {today}")#Import Syntax
There are several ways to import:
# Method 1: Import the whole module
import math
print(math.sqrt(16)) # must use math.sqrt()
# Method 2: Import specific items
from math import sqrt, pi
print(sqrt(16)) # can use sqrt() directly
print(pi) # 3.14159...
# Method 3: Import with an alias (nickname)
import datetime as dt # convention everyone follows
import statistics as stats
import xml.etree.ElementTree as ET # long names really benefit
# Method 4: Import everything (AVOID this)
# from math import * # BAD -- pollutes namespace, causes name conflicts#Why Aliases Matter
Some packages have a conventional short name that everybody uses. They are not required, but following them makes your code recognisable to any other Python programmer:
import datetime as dt # dates and times
import requests # fetching things over the internet
from pathlib import Path # importing one name from a module
from collections import Counter # another single-name import
# Your own long module names can be shortened the same way
# import my_project.helpers.formatting as fmtdt.date.today() in somebody's code, you know dt is datetime without scrolling up to check.HitModuleNotFoundErrororImportError?ModuleNotFoundError: No module named 'requests'means the package isn't installed in your current environment (pip install requests);ImportError: cannot import name 'X' from 'pkg'means the symbol moved or you typo'd. See the error decoder for the full fix flow.
#Creating Your Own Module
.py file is a module. Here is how you create and use one:# File: math_helpers.py
"""Custom math utility functions."""
PI = 3.14159265358979
def circle_area(radius):
"""Calculate the area of a circle."""
return PI * radius ** 2
def circle_circumference(radius):
"""Calculate the circumference of a circle."""
return 2 * PI * radius
def celsius_to_fahrenheit(celsius):
"""Convert Celsius to Fahrenheit."""
return celsius * 9 / 5 + 32
def fahrenheit_to_celsius(fahrenheit):
"""Convert Fahrenheit to Celsius."""
return (fahrenheit - 32) * 5 / 9# File: main.py (in the same directory)
import math_helpers
area = math_helpers.circle_area(5)
print(f"Area: {area:.2f}") # Area: 78.54
temp_f = math_helpers.celsius_to_fahrenheit(100)
print(f"100C = {temp_f}F") # 100C = 212.0F
# Or import specific functions
from math_helpers import circle_area, celsius_to_fahrenheit
print(circle_area(10)) # 314.16
print(celsius_to_fahrenheit(37)) # 98.6The __name__ Guard
__name__ to "__main__". When the file is imported as a module, __name__ is set to the module's name:# File: math_helpers.py
def circle_area(radius):
return 3.14159 * radius ** 2
# This block ONLY runs when the file is executed directly
# It does NOT run when the file is imported
if __name__ == "__main__":
# Test code -- great for quick testing
print("Running tests...")
print(f"Area of r=5: {circle_area(5):.2f}")
print(f"Area of r=10: {circle_area(10):.2f}")
print("All tests passed!")$ python math_helpers.py # __name__ == "__main__" --> tests run
Running tests...
Area of r=5: 78.54
Area of r=10: 314.16
All tests passed!
$ python main.py # math_helpers imported --> tests do NOT run
When you 'import math_helpers' in main.py, does the if __name__ == '__main__' block in math_helpers.py run?
#Packages and Directory Structure
__init__.py file:Project Package Structure
# In main.py, you can import from packages:
from data.loader import load_csv
from data.cleaner import normalize
from reports.summary import build_summary
from utils.dates import to_iso
# Or import the package and use dot notation
import reports.monthly
report = reports.monthly.build("2026-03")__init__.py file can be empty (just marks the directory as a package) or can contain initialization code and convenience imports.#pip and PyPI
# Install a package
pip install requests
# Install a specific version
pip install requests==2.32.3
# Install multiple packages
pip install requests rich pillow
# Upgrade a package
pip install --upgrade requests
# Uninstall a package
pip uninstall requests
# See what is installed
pip list
# Show details about a package
pip show requests
#Virtual Environments
# Create a virtual environment
python -m venv myproject_env
# Activate it (Mac/Linux)
source myproject_env/bin/activate
# Activate it (Windows)
myproject_env\Scripts\activate
# Now pip installs into THIS environment only
pip install requests rich
# See what is installed in this environment
pip list
# Deactivate when done
deactivate
#requirements.txt
requirements.txt file lists all packages a project needs, making it reproducible:# Generate requirements.txt from current environment
pip freeze > requirements.txt
# Install everything from requirements.txt
pip install -r requirements.txt
requirements.txt:requests==2.32.3
rich==13.7.1
pillow==10.2.0
python-dateutil==2.9.0
#Useful Standard Library Modules
Python's standard library is extensive. Here are modules you will use constantly:
# os -- file system operations
import os
print(os.listdir(".")) # list files in current directory
print(os.path.exists("data.csv")) # check if file exists
os.makedirs("output/plots", exist_ok=True) # create nested directories
# pathlib -- modern file paths (recommended over os.path)
from pathlib import Path
data_dir = Path("data")
csv_files = list(data_dir.glob("*.csv")) # find all CSV files
# collections -- specialized data structures
from collections import Counter, defaultdict
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
print(Counter(words)) # Counter({'apple': 3, 'banana': 2, 'cherry': 1})
# itertools -- efficient iteration tools
from itertools import combinations, product
print(list(combinations([1, 2, 3], 2))) # [(1,2), (1,3), (2,3)]
# time -- timing operations
import time
start = time.time()
# ... do something ...
elapsed = time.time() - start
print(f"Took {elapsed:.3f} seconds")
# copy -- deep vs shallow copy
import copy
original = [[1, 2], [3, 4]]
shallow = original.copy() # nested lists are still shared
deep = copy.deepcopy(original) # completely independent copy#Code Playground
Tests · Build modules, import from them, and test that each function works correctly!
Interactive Lab
See how importing modules changes what's available in your namespace, and trace import errors step by step
#Key Takeaways
import modulegives you access to a module's contents — usemodule.function()to call its functions. Usefrom module import functionfor direct access without the prefix- Some aliases are conventions everyone follows —
import datetime as dtis recognised instantly; inventing your own alias is not wrong if __name__ == "__main__":guards script-only code — put tests and demos inside this block so they run when executed directly but not when imported as a module- Virtual environments isolate project dependencies — use
python -m venv .venvfor every project. This prevents version conflicts between projects requirements.txtmakes projects reproducible —pip freeze > requirements.txtsaves your exact versions,pip install -r requirements.txtrecreates the environment. Always include it in your repo
What is the correct way to import the datetime module with an alias?