Lesson 8

Modules & packages

Home › Courses › Python › Lesson 8
🐍 Lesson 8 · 15 minutes

Modules & packages.
Stop writing from scratch.

You've been writing code in one file. Real Python projects spread code across dozens of files, and lean on hundreds of packages written by other people. This lesson teaches you to split your own code into modules, install third-party packages safely, and manage dependencies so your project runs identically on any machine.

~15 minutes Beginner → Intermediate Python 3.x
Python path Lesson 7 import Stdlib pip venv requirements Challenge

A module is a .py file you import with import name. A package is a directory of modules with an init.py. Python ships with hundreds of built-in modules — os, json, datetime, random, re. Install third-party packages with pip install name inside a virtual environment. Pin your project's dependencies in requirements.txt so anyone can recreate the environment.

Before this lesson: you should be comfortable with functions, files, and error handling. If not, read Lesson 4, Lesson 6, and Lesson 7 first.

📥 import — using modules

Four forms. Learn all four, use the first one most.

Python
# 1. import module — access as module.name
import math
print(math.sqrt(16)) # 4.0
print(math.pi) # 3.141592653589793

# 2. import with alias — useful for long names
import datetime as dt
now = dt.datetime.now()
print(now.year) # 2026

# 3. from module import specific name
from math import sqrt, pi
print(sqrt(25)) # 5.0
print(pi) # 3.141592653589793

# 4. from module import name with alias
from random import randint as rnd
print(rnd(1, 10)) # random int between 1 and 10

# AVOID: from module import *
# This dumps everything into your namespace and makes code hard to trace.

import math

Best default. You always know where a name came from: math.sqrt, math.pi. Slightly more typing, way less confusion when reading.

from math import sqrt

Good when the name is unambiguous — sqrt(), pi, sleep. Bad when the name is generic like open or list.

Aliases

Common conventions: import numpy as np, import pandas as pd, import matplotlib.pyplot as plt. Follow the community convention — readers expect it.

Never from x import *

It imports every public name, hides what's available, can shadow built-ins, and makes "where did this function come from?" unanswerable. PEP 8 explicitly discourages it.

📦 The standard library — free stuff in the box

These 8 modules cover 80% of what you'll need before installing anything.

Python
# os — filesystem paths, environment
import os
print(os.getcwd()) # current directory
print(os.listdir(".")) # files in current dir

# pathlib — modern path handling (preferred over os.path)
from pathlib import Path
p = Path("data") / "file.txt"
print(p.exists())

# json — read/write JSON
import json
data = json.dumps({"a": 1}) # '{"a": 1}'

# datetime — dates and times
from datetime import datetime, timedelta
print(datetime.now())
print(datetime.now() + timedelta(days=7)) # a week from now

# random — random numbers and choices
import random
print(random.choice(["a", "b", "c"]))
print(random.randint(1, 100))

# re — regular expressions
import re
emails = re.findall(r"\S+@\S+", "contact a@b.com or c@d.org")

# time — sleep, timestamps
import time
print(time.time()) # unix timestamp
# time.sleep(1) # wait 1 second

# collections — Counter, defaultdict, namedtuple
from collections import Counter
counts = Counter(["a", "b", "a"])
print(counts) # Counter({'a': 2, 'b': 1})

# sys — system info, argv, exit
import sys
print(sys.version)
# sys.exit(1) # exit with error code

Check the stdlib before pip. 80% of "I need a package for this" is already solved by pathlib, collections, itertools, functools, re, or json. Search python.org/3/library; first — the standard library is tested, dependency-free, and won't disappear. Every third-party package is a future liability.

🔧 pip — installing third-party packages

The Python Package Index has 500,000+ packages. These 6 commands cover everything.

Terminal
# Install a package
pip install requests

# Install a specific version
pip install requests==2.31.0

# Upgrade to latest
pip install --upgrade requests

# Uninstall
pip uninstall requests

# See what's installed
pip list

# See info about one package
pip show requests

Never pip install outside a virtual environment. On Mac/Linux this either fails with a permission error (good — the system protects itself) or, if you use sudo pip, corrupts the system Python (bad — real damage). On Windows it can work but pollutes your global install. The rule is: activate your venv first, then pip install. Always.

Popular packages you'll likely install first. requests (HTTP), pandas (dataframes), numpy (arrays), beautifulsoup4 (HTML parsing), pytest (testing), black (code formatter), ruff (linter). Those seven cover most beginner projects beyond the standard library.

🧊 venv — one environment per project

The single most important habit for working with packages.

Every project gets its own virtual environment — a folder containing its own Python interpreter and its own installed packages. Project A can use requests 2.31 while project B uses requests 1.0. Without venvs, they'd conflict and one would break.

Terminal
# 1. Create the project folder
mkdir my-project
cd my-project

# 2. Create a virtual environment called "venv"
python3 -m venv venv
# Windows: python -m venv venv

# 3. Activate it
source venv/bin/activate
# Windows Command Prompt: venv\Scripts\activate
# Windows PowerShell: .\venv\Scripts\Activate.ps1

# You'll see (venv) appear at the start of your prompt.

# 4. Now pip install goes into THIS project, not the system
pip install requests

# 5. When you're done, deactivate
deactivate

# To resume work later, activate again
source venv/bin/activate

venv/ is gitignored

Never commit the venv folder. It contains the interpreter and 50MB+ of packages. Add venv/ to .gitignore. It's recreated in one command on any machine.

One venv per project

Not one per machine. Not shared between projects. The whole point is isolation. If you have two projects, you have two venvs.

Prompt shows it's active

When active, your terminal prompt gets (venv) prefixed. When you see that, you know pip install will land in this project. When you don't, it won't.

Forgot to activate?

If pip install gives a permission error, you're installing system-wide. Run the activate command for your OS, then retry. The permission error was Python protecting itself.

📋 requirements.txt — recreate your environment anywhere

How you share a project with a coworker, a server, or future-you.

Terminal
# After installing everything your project needs, freeze it
pip freeze > requirements.txt

# requirements.txt now looks like this:
requests2.31.0
beautifulsoup44.12.2
lxml==4.9.3

# Someone else (or future-you on a new machine) runs:
pip install -r requirements.txt
# Exactly those versions are installed. Same behavior guaranteed.

Commit it

requirements.txt goes in git. The venv doesn't. Together, they mean anyone can clone your repo and get a working environment.

Pin exact versions

pip freeze writes requests==2.31.0, not requests. Exact versions prevent "it worked yesterday" bugs when a dependency silently updates.

pip freeze vs pip list

pip list shows everything installed. pip freeze outputs installable requirements format. Use freeze to make requirements.txt.

Multiple requirements files

Larger projects split into requirements.txt (production) and requirements-dev.txt (tests, linters). Install dev with -r requirements.txt -r requirements-dev.txt.

💻 A complete example

Split one script into two modules and install a real package.

File structure
my-project/
├── venv/ (created by python -m venv)
├── helpers.py (our helper module)
├── main.py (the entry point)
└── requirements.txt (pinned dependencies)
helpers.py
# helpers.py — reusable functions

def count_words(text):
"""Return a dict of {word: count} for the text."""
counts = {}
for word in text.lower().split():
counts[word] = counts.get(word, 0) + 1
return counts

def top_words(counts, n=3):
"""Return the top N (word, count) pairs."""
return sorted(counts.items(), key=lambda kv: kv[1], reverse=True)[:n]
main.py
# main.py — the entry point
from helpers import count_words, top_words

text = "the cat sat the cat ran the cat slept"
counts = count_words(text)
for word, count in top_words(counts):
print(f"{word}: {count}")

# Output:
# the: 3
# cat: 3
# sat: 1

Notice: no path manipulation needed. from helpers import count_words works because helpers.py is in the same directory as main.py, and Python automatically adds the script's directory to the import path. When your code grows, you'll move helpers into a package (my_project/helpers.py with init.py), but the import syntax stays the same.

Terminal
cd my-project
python3 -m venv venv
source venv/bin/activate
python main.py
the: 3
cat: 3
sat: 1

🏆 Challenge

Three tasks. Each builds on the last.

Task 1 — Split a file

Take any script you've written (or the word-counter from Lesson 5). Split it into two files: utils.py for the helper functions, main.py for the script that uses them. Import the helpers into main and run it.

Task 2 — First pip install

In a fresh project folder:

1. Create a venv: python -m venv venv.
2. Activate it.
3. Install requests: pip install requests.
4. Write a script that fetches https://api.github.com and prints the status code (use requests.get(...).status_code).
5. Freeze: pip freeze > requirements.txt.
6. Deactivate, then re-activate and check pip list shows requests.

Task 3 — Two projects, two venvs

The point of venvs is isolation. Prove it:

1. Create project A with a venv, install requests2.31.0.
2. Create project B with its own venv, install requests2.28.0.
3. Run pip show requests in each. Both versions coexist.

Without venvs, this would be impossible — installing one version overwrites the other. That's the entire reason venvs exist.

⚠️ Common mistakes

The eight module and package errors that trip up every beginner.

1. Installing packages without a venv

System-wide installs either fail (Mac/Linux) or pollute everything (Windows). Fix: always python -m venv venv and activate before installing anything.

2. Using sudo pip

sudo pip install corrupts the system Python on Mac/Linux. It breaks tools that depend on the system's packages, and it's nearly impossible to undo. If you get permission denied, activate your venv.

3. Forgetting to commit requirements.txt

Your code works on your machine. A coworker clones it, runs it, and gets ModuleNotFoundError: No module named 'requests'. Fix: always pip freeze > requirements.txt and commit the file.

4. from module import *

Dumps every public name into your namespace. Shadows built-ins. Makes it impossible to know what's available. PEP 8 forbids it. Fix: import specific names, or import the module and use module.name.

5. Naming your file the same as a module

A file called random.py in your project shadows Python's built-in random module. import random now imports your file. Everything breaks mysteriously. Fix: never name a file after a stdlib module.

6. Circular imports

a.py imports from b.py, and b.py imports from a.py. Python crashes with ImportError: cannot import name 'x' from partially initialized module. Fix: refactor so one file doesn't depend on the other, or move shared code into a third module.

7. Not pinning versions

requirements.txt that says requests without a version means "any version" — and yesterday's working code breaks when the package updates. Fix: pip freeze pins exactly; use it.

8. ModuleNotFoundError in a script

You wrote import helpers but the script runs from a different directory. Python looks for helpers in the current working directory and the script's directory. Fix: run scripts from the folder they live in (cd first), or use packages with relative imports.

➡️ What's next

Two paths now: keep learning Python, or build a real project.

❓ Frequently asked questions

What is a module in Python?

A module is a .py file containing Python code — functions, classes, variables — that can be imported into another file. Every .py file you write is already a module. Python also ships with hundreds of built-in modules (os, json, datetime, random, re) and the PyPI ecosystem has 500,000+ third-party modules you can install with pip.

What is the difference between a module and a package?

A module is a single .py file. A package is a directory of modules with an init.py file that marks it as a package. A package can contain sub-packages, forming a tree. Example: the json package (directory) contains modules like json.encoder and json.decoder. In practice, most people say 'package' for both — the distinction matters only when you're writing one.

What is the difference between import X and from X import Y?

import X gives you the whole module — you access names as X.func(). from X import Y imports a specific name into your namespace — you call Y() directly. Prefer import X for clarity (you can always tell where a name came from). Use from X import Y for very common names or when aliasing is obvious. Avoid from X import * — it dumps everything into your namespace and makes code hard to trace.

How do I install a Python package?

Use pip: pip install package_name. First activate your virtual environment — never install into the system Python. To install a specific version: pip install package_name1.2.3. To upgrade: pip install --upgrade package_name. To uninstall: pip uninstall package_name. To see what's installed: pip list.

What is a virtual environment and why do I need one?

A virtual environment is an isolated folder containing its own Python interpreter and its own set of installed packages. Without one, all projects share the same packages — and if project A needs requests 2.0 while project B needs requests 1.0, both can't be installed at once. Create with: python -m venv venv. Activate with: source venv/bin/activate (Mac/Linux) or venv\Scripts\activate (Windows). Always use one.

What is requirements.txt?;

requirements.txt lists the packages your project needs so anyone can recreate your environment. Generate with: pip freeze > requirements.txt. Install from it with: pip install -r requirements.txt. The file contains pinned versions (requests2.31.0) to guarantee reproducible installs. Include it in your git repo — it's how your code becomes runnable on someone else's machine.

What is init.py and do I need it?

init.py is a file that turns a directory into a Python package. It runs automatically when the package is imported and is often used to expose specific names, set up logging, or define all. In Python 3.3+, implicit namespace packages work without init.py — but explicit packages with init.py are still the norm for published packages and clearer for beginners. Add one.

What is the difference between pip and pip3?

Nothing — on most systems, pip3 is just an alias for pip that makes it explicit you're installing for Python 3 (since pip used to exist for Python 2). On macOS and Linux, use pip3 to be safe. On Windows, pip works fine. Inside a virtual environment, pip always refers to the environment's Python, so either name works.

🌐 External resources

← Back to Python track
You're in the Python track · Lesson 8