Previous | Tutorial index | Next
Describe modules.
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.
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.
Every single Python file you create is a potential module that other Python files can use.
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. |
The name of your module file becomes its identity when you import it.
Rules (Must follow):
_).if, for, while, def, class, import, etc.).Conventions (Good practice):
data_processor.py, file_handler.py).my-module.py is invalid because of the hyphen; Python interprets it as a subtraction).| 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 |
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.
As a programmer, you will interact with three different categories of modules:
Built-in Modules:
sys (system functions), math (basic math), time (time functions).Standard Library Modules:
os (operating system interface), json (JSON data handling), re (regular expressions), csv (spreadsheet files).Third-Party Modules:
pip (e.g., pip install requests) before you can import them.numpy (numerical computing), pandas (data analysis), django (web framework).Local/User-defined Modules:
.py files you create yourself for your own projects.When you type import something, Python searches for something.py in specific locations, in this specific order:
PYTHONPATH environment variable (if set).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.
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
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
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.
Q4: Which keyword does Python use to bring a module into your current file?
A) include
B) using
C) import
D) require
Q5: What is the key difference between a Standard Library module and a Third-Party module?
Time to get your hands dirty! These exercises are designed to be done in a lab session or during self-study.
Goal: Create a simple module, import it in the Python interactive shell, and inspect its contents.
Instructions:
Open your code editor (like VS Code, PyCharm, or even Notepad).
Create a new file and name it greetings.py.
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!")
Save the file in a folder (e.g., C:\my_modules or ~/Desktop/modules_practice).
Open your terminal/command prompt and navigate to that exact folder using the cd command.
Type python to open the interactive interpreter.
Type import greetings and press Enter. Notice that nothing "happens" (no prints) – that's fine!
Type greetings.say_hello() and observe the output.
Type print(greetings.AUTHOR) and observe the output.
Try to call say_goodbye() directly (without greetings.). What error do you get? Why?
Goal: Practice importing built-in modules and using the dir() function to explore what they offer.
Instructions:
import math.dir(math). This lists all the names (functions, constants, etc.) available inside the math module. Scroll through the list.help(math.sqrt) to read the documentation for the square root function.math module to calculate the square root of 49.random module. Use random.randint(1, 10) to generate a random number between 1 and 10.Goal: Prove that a module's executable code runs only once.
Instructions:
Create a file called counter.py and write this inside:
# counter.py
print("Counter module is initializing...")
LOAD_COUNT = 1
Save the file.
Open the Python interpreter in the same folder.
Type import counter – you will see the print message.
Type import counter a second time. Notice the print message does not appear again!
Type counter.LOAD_COUNT to see that the variable is still there. Python cached the module after the first import.
These are longer, more challenging questions to solidify your understanding of modules conceptually and practically.
Task:
Create a module named profile.py. Inside this module:
NAME = "Alex", AGE = 30, and CITY = "New York".display() that prints all this information in a neatly formatted string.have_birthday() that increments the AGE variable by 1 and prints a "Happy Birthday!" message.Now, create a second Python script (e.g., main.py) in the same directory. In main.py:
profile module.display() function.have_birthday() function.display() again to show the updated age.Submission: Provide the code for profile.py and main.py.
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
sys.modulesTask:
When you import a module, Python stores it in a global dictionary called sys.modules.
math and random.sys.modules.sys.modules does and why it is important for performance (hint: think about the "caching" concept from Exercise 3).Submission: Provide your Python code and your written explanation.
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.
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.
python order_test.py) and see all 4 steps.Submission: Write down the console outputs for both scenarios and explain your reasoning.
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().)
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.
accounts.py, transactions.py, etc.).Submission: Submit your design as a bulleted list or a short table.
| 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.
.py file containing Python definitions and statements.sys.path) to locate modules.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.