Previous | Tutorial index | Next

Tutorial 1: Understanding Modules – The Building Blocks of Python Code

Learning Objectives

Describe modules.

1. Introduction: Why Modules Matter

When you start writing Python programs, you usually put all your code in a single .py file. This works fine for small scripts. However, as your projects grow, a single file becomes messy, hard to navigate, and difficult to reuse.

Imagine writing a calculator app. You wouldn't want to rewrite the addition and subtraction logic every time you start a new project. Modules solve this problem. They allow you to save reusable code in separate files and bring that code into new projects whenever you need it.

2. What Exactly is a Module?

In Python, a module is simply a file containing Python code. The file must have the .py extension.

Inside this file, you can define:

Technical Definition: A module is an object of type module that serves as an organizational unit for code. The filename (excluding the .py) becomes the module's name.

Key Point

Every single Python file you create is a potential module that other Python files can use.

3. The Three Core Roles of Modules

Modules are not just about saving code; they serve three major purposes in professional software development:

Role Explanation
1. Code Reusability (DRY) Write a piece of code once, save it in a module, and reuse it in hundreds of different programs without copying and pasting. This follows the "Don't Repeat Yourself" (DRY) principle.
2. Code Organization Modules allow you to group related functionality. For example, put all math-related functions in math_utils.py and all string-related functions in string_utils.py. This makes your project structure logical and easy to navigate.
3. Preventing Naming Conflicts If you define a function called calculate() and a friend gives you a library that also has a calculate(), they will clash. Modules create separate "namespaces". You can call my_module.calculate() and friends_module.calculate() separately, so Python knows exactly which one you mean.

4. Module Naming Rules and Conventions

The name of your module file becomes its identity when you import it.

Rules (Must follow):

Conventions (Good practice):

Valid Module Names Invalid Module Names Why Invalid?
calculator.py 123calc.py Starts with a digit
my_math.py my-math.py Hyphen is not allowed in identifiers
utils.py def.py def is a reserved keyword
_helper.py helper tool.py Contains a space

5. What Can a Module Contain?

A module can contain any valid Python constructs. Let's look at a concrete example.

Create a file named sample.py:

# 1. Variables (data) PI = 3.14159 AUTHOR = "Jane Doe" # 2. Functions (behavior) def greet(name): return f"Hello, {name}!" def add(a, b): return a + b # 3. Classes (blueprints for objects) class Student: def __init__(self, name): self.name = name def introduce(self): return f"I am {self.name}" # 4. Executable statements (code that runs on import) print(f"Module '{__name__}' has been loaded!")

Important Note about Executable Statements: When you import a module for the first time in a program, Python runs all the top-level executable statements (like that print function) immediately. If you import the same module again later in the same program, Python does not re-run the code; it uses a cached version to save time.

6. Types of Modules in Python

As a programmer, you will interact with three different categories of modules:

  1. Built-in Modules:

  2. Standard Library Modules:

  3. Third-Party Modules:

  4. Local/User-defined Modules:

7. How Does Python Find Your Module? (The Search Path)

When you type import something, Python searches for something.py in specific locations, in this specific order:

  1. The current directory (where your main script is running).
  2. Directories listed in the PYTHONPATH environment variable (if set).
  3. The installation-dependent default paths (e.g., C:\Python312\Lib on Windows, or /usr/lib/python3.12/ on Linux).

You can view this search path by importing the sys module and printing sys.path:

import sys print(sys.path)

If your module is not in one of these directories, Python will raise a ModuleNotFoundError.

8. Quiz: Check Your Understanding (Part 1)

Test your foundational knowledge before proceeding to the practical exercises.


Q1: What file extension must a Python module have?

A) .py B) .python C) .mod D) .txt

AnswerA) `.py`

Q2: Which of the following is a valid name for a Python module file?

A) 2nd_calc.py B) my-calc.py C) my_calc.py D) class.py

AnswerC) `my_calc.py` (starts with a letter, no spaces, no hyphens, not a keyword)

Q3: True or False: If you define a function multiply() in a module named tools.py, and a different module also defines multiply(), you cannot use both in the same program.

AnswerFalse. You can use `tools.multiply()` and `other.multiply()` to differentiate them.

Q4: Which keyword does Python use to bring a module into your current file?

A) include B) using C) import D) require

AnswerC) `import`

Q5: What is the key difference between a Standard Library module and a Third-Party module?

AnswerStandard Library modules come pre-installed with Python. Third-party modules must be installed using a package manager like `pip` before you can import them.

9. Hands-on Exercises (Practical Tasks)

Time to get your hands dirty! These exercises are designed to be done in a lab session or during self-study.


Exercise 1: Your First Module

Goal: Create a simple module, import it in the Python interactive shell, and inspect its contents.

Instructions:

  1. Open your code editor (like VS Code, PyCharm, or even Notepad).

  2. Create a new file and name it greetings.py.

  3. Inside greetings.py, type the following exactly:

    # greetings.py AUTHOR = "Your Full Name" def say_hello(): print("Hello, world!") def say_goodbye(): print("Goodbye, world!")
  4. Save the file in a folder (e.g., C:\my_modules or ~/Desktop/modules_practice).

  5. Open your terminal/command prompt and navigate to that exact folder using the cd command.

  6. Type python to open the interactive interpreter.

  7. Type import greetings and press Enter. Notice that nothing "happens" (no prints) – that's fine!

  8. Type greetings.say_hello() and observe the output.

  9. Type print(greetings.AUTHOR) and observe the output.

  10. Try to call say_goodbye() directly (without greetings.). What error do you get? Why?

Sample Output ``` >>> import greetings >>> greetings.say_hello() Hello, world! >>> print(greetings.AUTHOR) Your Full Name >>> say_goodbye() Traceback (most recent call last): File "", line 1, in NameError: name 'say_goodbye' is not defined ``` The error occurs because `say_goodbye` is inside the `greetings` namespace and must be accessed with `greetings.say_goodbye()`.

Exercise 2: Exploring Built-in Modules

Goal: Practice importing built-in modules and using the dir() function to explore what they offer.

Instructions:

  1. In the Python interactive shell, type import math.
  2. Type dir(math). This lists all the names (functions, constants, etc.) available inside the math module. Scroll through the list.
  3. Type help(math.sqrt) to read the documentation for the square root function.
  4. Use the math module to calculate the square root of 49.
  5. Import the random module. Use random.randint(1, 10) to generate a random number between 1 and 10.
Sample Output ``` >>> import math >>> dir(math) ['__doc__', '__loader__', '__name__', '__package__', ..., 'acos', 'acosh', 'asin', ..., 'pi', 'sqrt', ...] >>> help(math.sqrt) Help on built-in function sqrt in module math: sqrt(x, /) Return the square root of x. >>> math.sqrt(49) 7.0 >>> import random >>> random.randint(1, 10) 7 ```

Exercise 3: Understanding Module Caching

Goal: Prove that a module's executable code runs only once.

Instructions:

  1. Create a file called counter.py and write this inside:

    # counter.py print("Counter module is initializing...") LOAD_COUNT = 1
  2. Save the file.

  3. Open the Python interpreter in the same folder.

  4. Type import counter – you will see the print message.

  5. Type import counter a second time. Notice the print message does not appear again!

  6. Type counter.LOAD_COUNT to see that the variable is still there. Python cached the module after the first import.

Sample Output ``` >>> import counter Counter module is initializing... >>> import counter >>> counter.LOAD_COUNT 1 ``` The print statement only appears once, proving that the module code is executed only on the first import.

10. Homework Questions (Take-Home Assignment)

These are longer, more challenging questions to solidify your understanding of modules conceptually and practically.


Homework Question 1: Building a Profile Module

Task: Create a module named profile.py. Inside this module:

Now, create a second Python script (e.g., main.py) in the same directory. In main.py:

Submission: Provide the code for profile.py and main.py.

Sample Solution

profile.py:

# profile.py NAME = "Alex" AGE = 30 CITY = "New York" def display(): print(f"Name: {NAME}, Age: {AGE}, City: {CITY}") def have_birthday(): global AGE AGE += 1 print("Happy Birthday!")

main.py:

# main.py import profile profile.display() # Name: Alex, Age: 30, City: New York profile.have_birthday() # Happy Birthday! profile.display() # Name: Alex, Age: 31, City: New York

Homework Question 2: Researching sys.modules

Task: When you import a module, Python stores it in a global dictionary called sys.modules.

Submission: Provide your Python code and your written explanation.

Sample Solution
import sys import math import random print("Modules currently loaded:") for module_name in sys.modules.keys(): print(module_name)

Explanation: sys.modules is a dictionary that caches all modules that have already been imported in the current Python session. This cache is crucial for performance because it prevents Python from reloading and re-executing the same module multiple times. When you import a module a second time, Python simply returns the reference from sys.modules instead of searching for and reloading the file, which saves time and prevents infinite recursion in circular imports. This caching mechanism is why the print statement in Exercise 3 only appeared once.


Homework Question 3: The Execution Order Behavior

Task: Create a module named order_test.py with the following content:

print("Step 1: Top-level print") def my_func(): print("Step 3: Inside the function") print("Step 2: Another top-level print") if __name__ == "__main__": print("Step 4: Inside the main guard")

Now, open a Python interpreter, import order_test twice, and write down the exact console output you see.

Submission: Write down the console outputs for both scenarios and explain your reasoning.

Sample Solution

Console output when imported:

>>> import order_test Step 1: Top-level print Step 2: Another top-level print >>> import order_test

Why Step 4 doesn't appear: The if __name__ == "__main__": block only executes when the module is run directly (e.g., python order_test.py), not when it is imported. When imported, the module's __name__ is 'order_test', not '__main__'.

To see all 4 steps when running directly:

python order_test.py

Output:

Step 1: Top-level print Step 2: Another top-level print Step 4: Inside the main guard

(The function my_func() is not called, so Step 3 never executes. To see Step 3, you would need to call my_func().)


Homework Question 4: Planning a Project Structure

Task: Imagine you are building a simple Banking System. The system needs to handle:

Assignment: Without writing any code, design a modular structure for this project.

Submission: Submit your design as a bulleted list or a short table.

Sample Solution
Module Name Contents Purpose
accounts.py create_account(), close_account(), get_account_balance() Manages the lifecycle of bank accounts.
transactions.py deposit(), withdraw(), transfer(), get_transaction_history() Handles all financial transactions between accounts.
validation.py validate_account_number(), validate_amount(), validate_pin() Ensures all inputs meet security and format requirements before processing.

Rationale: Separating accounts, transactions, and validation into distinct modules adheres to the Single Responsibility Principle. Each module has a clear, focused purpose, making the code easier to understand, test, and maintain. A developer can modify transaction logic without risking changes to account creation, and validation logic can be reused across both modules. This organization also makes it easier for multiple developers to work on the project simultaneously without causing merge conflicts.

11. Summary of Tutorial 1

This concludes the Tutorial 1. Once you fully understand these concepts, you are ready to move on to Tutorial 2, where we will dive into packages and their file system structures.

Previous | Tutorial index | Next