Day 12: Modules, Packages & Virtual Environments
🐍 Day 12: Modules, Packages & Virtual Environments
1. Learning Objectives
By the end of Day 12, you will be able to:
- Split your code into modules (separate
.pyfiles) for better organization - Import and use functions, classes, and variables from other modules
- Understand the different types of import statements
- Create and use packages (directories of modules with
__init__.py) - Set up a virtual environment to isolate project dependencies
- Use the
if __name__ == "__main__"guard correctly - Install third‑party packages with
pip
2. Concept Explanation
2.1 Why Modules? — Breaking Up Is Smart
As your programs grow, keeping everything in one file becomes unmanageable. Modules let you split code into logically separate files.
Analogy: A module is like a chapter of a book — each file contains a well‑defined part of the program, and you can import the parts you need.
project/
├── main.py # The entry point
├── utils.py # Helper functions
└── data_loader.py # Data reading functions
2.2 Creating and Using a Module
Any .py file is automatically a module. For example, create utils.py:
# utils.py
def greet(name):
return f"Hello, {name}!"
PI = 3.14159
Now in main.py you can import it:
# main.py
import utils
print(utils.greet("Alice")) # Hello, Alice!
print(utils.PI) # 3.14159
When you do import utils, Python:
- Looks for
utils.pyin the same directory, then in the Python path. - Executes the file (defines functions, variables, etc.).
- Makes those names available via the module name
utils.
2.3 Import Styles
There are several ways to import; each has its use.
| Syntax | Example | How to access |
|---|---|---|
import module | import math | math.sqrt(16) |
import module as alias | import numpy as np | np.array([1,2]) |
from module import name | from math import sqrt | sqrt(16) directly |
from module import name as alias | from math import sqrt as root | root(16) |
from module import * (⚠️ avoid) | from math import * | pi, sin — pollutes namespace |
Best practices:
- Prefer
import modulefor standard library — keeps namespacing clear. - Use
from module import specific_namewhen you need only a few things. - Never use
from module import *in production code — it makes code unreadable and can cause name clashes.
# Good
import math
print(math.floor(2.3))
# Also good
from math import floor, ceil
print(floor(2.3))
# Bad
from math import *
2.4 Packages — Modules Inside Folders
A package is a directory with a special __init__.py file (it can be empty). Packages allow you to group related modules.
shapes/
├── __init__.py # Marks this folder as a package
├── circle.py
├── square.py
└── triangle.py
__init__.py can also execute initialization code or define what from package import * should import (via a __all__ list).
Importing from a package:
# main.py
import shapes.circle # Full path
from shapes import square # Direct import
from shapes.circle import area # Specific function
Inside circle.py you can also use relative imports (for internal package organization):
# shapes/circle.py
from . import square # relative import: from same package
2.5 The if __name__ == "__main__" Guard
When you run a Python file directly, its __name__ variable is set to "__main__". When the file is imported as a module, __name__ is set to the module's name.
This lets you include code that only runs when the file is executed directly, not when imported.
# calculator.py
def add(a, b):
return a + b
def subtract(a, b):
return a - b
if __name__ == "__main__":
# This block runs only when you do: python calculator.py
print("Testing calculator:")
print(add(2, 3)) # 5
If you import calculator from another script, the test code inside the if block is ignored.
Rule of thumb: Always wrap script‑like code inside if __name__ == "__main__": to keep modules importable and reusable.
2.6 Virtual Environments — Isolate Your Dependencies
Different projects may need different versions of the same library. Virtual environments create an isolated Python setup per project.
Create a virtual environment:
# Windows
python -m venv myproject_env
# macOS / Linux
python3 -m venv myproject_env
Activate:
# Windows (Command Prompt)
myproject_env\Scripts\activate
# macOS / Linux
source myproject_env/bin/activate
After activation, pip installs packages locally into that environment, and your terminal prompt changes to show the active environment.
Deactivate: simply type deactivate.
Freezing requirements — share your environment:
pip freeze > requirements.txt
This saves all installed packages and their versions. Another developer can recreate the environment with:
pip install -r requirements.txt
2.7 Where Python Looks for Modules
Python searches for modules in the directories listed in sys.path. You can see them:
import sys
print(sys.path)
It includes:
- The directory containing the input script.
- The environment variable
PYTHONPATH. - Installation‑dependent default paths (including
site‑packagesfor installed packages).
You can add a directory temporarily:
import sys
sys.path.append("/my/custom/path")
But for permanent modification, use a proper package structure or install your code in development mode (pip install -e .).
2.8 Common Mistakes
| Mistake | Consequence |
|---|---|
| Circular imports (A imports B, B imports A) | May cause ImportError or unexpected behavior |
| Naming a script the same as a standard library | Shadows the built‑in module (e.g., random.py) |
| Forgetting to activate the virtual environment | Packages installed globally, causing version conflicts |
Using from module import * | Pollutes namespace, makes code hard to debug |
| Running a script that expects to be a module without the guard | Running it directly executes its test code unintentionally |
3. Code Examples
Example 1: A Utility Module
# file: string_utils.py
def reverse_words(text):
"""Reverse the order of words in a sentence."""
return ' '.join(text.split()[::-1])
def count_vowels(text):
"""Count the number of vowels in a string."""
return sum(1 for c in text.lower() if c in 'aeiou')
if __name__ == "__main__":
# Quick manual tests
print(reverse_words("Hello world")) # world Hello
print(count_vowels("Hello")) # 2
Example 2: Using a Package
# shapes/__init__.py
# (empty)
# shapes/circle.py
import math
def area(radius):
return math.pi * radius ** 2
def circumference(radius):
return 2 * math.pi * radius
# main.py
from shapes.circle import area, circumference
print(f"Area: {area(5):.2f}") # 78.54
print(f"Circumference: {circumference(5):.2f}") # 31.42
Example 3: Import with Alias
import numpy as np
arr = np.array([1, 2, 3])
print(arr)
4. Hands-On Exercises
Exercise 1: Create and Import a Module
Create a file temperature.py with two functions: celsius_to_fahrenheit(c) and fahrenheit_to_celsius(f). In a separate main.py, import that module and convert 25°C and 77°F.
Exercise 2: Use an Alias
Import the math module with the alias m. Use it to compute the square root of 144 and the sine of m.pi/2.
Exercise 3: Build a Simple Package
Create a directory calculator with an __init__.py and two modules: add_sub.py (with add/subtract functions) and mul_div.py (with multiply/divide functions). Write a main.py that imports both modules and demonstrates each operation.
Exercise 4: The __name__ Guard
Create a file greetings.py with a function hello(name) that returns "Hello, name!". Add an if __name__ == "__main__": block that prints hello("Tester"). Run the file directly, then import it from another script and see what happens.
Exercise 5: Virtual Environment Practice
Using the terminal:
- Create a virtual environment named
test_env. - Activate it.
- Install the
requestslibrary withpip install requests. - Freeze the requirements into
requirements.txt. - Deactivate the environment.
5. Applied Challenge Task 🏗️
Modular Contact Manager
Transform the contact manager from Day 7 into a well‑structured project with modules and a package.
Project structure:
contact_app/
├── main.py # Entry point
├── contacts/
│ ├── __init__.py
│ ├── operations.py # add, search, delete, list functions
│ └── storage.py # save_to_file, load_from_file functions
└── requirements.txt
Requirements:
operations.pycontains all the logic for managing contacts (stored in a dictionary passed as an argument). No direct I/O inside these functions.storage.pyhandles reading/writing the contacts to a JSON file (use thejsonmodule). It should exportload_contacts()andsave_contacts(contacts).main.pyties everything together: it loads contacts at startup, shows the menu, calls the appropriate operation functions, and saves before exiting.- Add the
if __name__ == "__main__"guard inmain.py. - Create a virtual environment and generate a
requirements.txt(even if empty or with onlyjsonalready built‑in, addpytestfor future testing).
Stretch goals:
- Add input validation inside
operations.py(e.g., phone number format) using custom exceptions. - Write a
README.mdexplaining how to set up the project. - Structure the menu inside a separate
cli.pymodule.
6. Brief Review Summary
| Concept | Key Points |
|---|---|
| Module | Any .py file; import with import module |
| Import styles | import, from…import, import…as; avoid * |
| Package | Directory with __init__.py; use dotted imports |
__name__ guard | if __name__ == "__main__": – run code only when script is executed directly |
| Virtual environment | python -m venv env; isolate dependencies |
pip freeze | Export installed packages to requirements.txt |
| Search path | sys.path – where Python looks for modules |
7. Preview of Next Topic — Day 13
Tomorrow we enter the world of Object‑Oriented Programming (OOP) — the paradigm that structures code around objects and classes.
- What are classes and objects?
- Defining classes with
class,__init__, and methods - Attributes (instance vs. class attributes)
- Creating and using objects
- The real‑world analogy that makes OOP intuitive
🎯 Your Action Items for Day 12:
- ✅ Complete all 5 exercises
- ✅ Build the Modular Contact Manager project
- ✅ Create at least one virtual environment and freeze its requirements
- ✅ Practice running the same file directly and importing it — observe the difference with
__name__
Comments
Post a Comment
Leave us your comments here...