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 .py files) 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:

  1. Looks for utils.py in the same directory, then in the Python path.
  2. Executes the file (defines functions, variables, etc.).
  3. Makes those names available via the module name utils.

2.3 Import Styles

There are several ways to import; each has its use.

SyntaxExampleHow to access
import moduleimport mathmath.sqrt(16)
import module as aliasimport numpy as npnp.array([1,2])
from module import namefrom math import sqrtsqrt(16) directly
from module import name as aliasfrom math import sqrt as rootroot(16)
from module import * (⚠️ avoid)from math import *pi, sin — pollutes namespace

Best practices:

  • Prefer import module for standard library — keeps namespacing clear.
  • Use from module import specific_name when 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‑packages for 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

MistakeConsequence
Circular imports (A imports B, B imports A)May cause ImportError or unexpected behavior
Naming a script the same as a standard libraryShadows the built‑in module (e.g., random.py)
Forgetting to activate the virtual environmentPackages 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 guardRunning 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:

  1. Create a virtual environment named test_env.
  2. Activate it.
  3. Install the requests library with pip install requests.
  4. Freeze the requirements into requirements.txt.
  5. 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:

  1. operations.py contains all the logic for managing contacts (stored in a dictionary passed as an argument). No direct I/O inside these functions.
  2. storage.py handles reading/writing the contacts to a JSON file (use the json module). It should export load_contacts() and save_contacts(contacts).
  3. main.py ties everything together: it loads contacts at startup, shows the menu, calls the appropriate operation functions, and saves before exiting.
  4. Add the if __name__ == "__main__" guard in main.py.
  5. Create a virtual environment and generate a requirements.txt (even if empty or with only json already built‑in, add pytest for future testing).

Stretch goals:

  • Add input validation inside operations.py (e.g., phone number format) using custom exceptions.
  • Write a README.md explaining how to set up the project.
  • Structure the menu inside a separate cli.py module.

6. Brief Review Summary

ConceptKey Points
ModuleAny .py file; import with import module
Import stylesimport, from…import, import…as; avoid *
PackageDirectory with __init__.py; use dotted imports
__name__ guardif __name__ == "__main__": – run code only when script is executed directly
Virtual environmentpython -m venv env; isolate dependencies
pip freezeExport installed packages to requirements.txt
Search pathsys.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:

  1. ✅ Complete all 5 exercises
  2. ✅ Build the Modular Contact Manager project
  3. ✅ Create at least one virtual environment and freeze its requirements
  4. ✅ Practice running the same file directly and importing it — observe the difference with __name__

Comments

Popular posts from this blog

Day 1: Welcome to Python — Your Journey Begins

Python: Your Gateway to Coding Adventures

Day 11: List Comprehensions & Lambda Functions

Earn From the Comfort of Your Home

Build the skills to work comfortably from home, on your own terms.

Get Started Today