Module 12: Modules and Packages in Python
As Python programs become larger, keeping every function, class, and piece of logic in one file quickly becomes difficult to manage.
Python provides modules and packages to help developers organize code into smaller, reusable components.
A module is a Python file containing reusable code, while a package is a directory used to organize related modules.
By the end of this module, you will understand:
- What Python modules are
- What Python packages are
- How
importworks - How to use
from ... import - How to create aliases with
as - How to create your own modules
- How to organize modules into packages
- How
__init__.pyworks - Why
if __name__ == "__main__":is useful - How to use important Python Standard Library modules
- How to create practical projects using modules and packages
- Common import errors and how to fix them
What Is a Python Module?
A module is a Python file with a .py extension that contains reusable code.
A module can contain:
- Variables
- Functions
- Classes
- Constants
- Statements
- Other executable code
For example, create a file named:
1calculator.py
Add some functions:
1def add(a, b): 2 return a + b 3 4 5def subtract(a, b): 6 return a - b
Now create another file:
1main.py
You can import and use the functions from calculator.py:
1import calculator 2 3print(calculator.add(10, 20)) 4print(calculator.subtract(20, 5))
Output:
130 215
The important idea is that the code from calculator.py can be reused without rewriting the functions in main.py.
Why Use Modules?
Modules make Python projects easier to develop and maintain.
Without modules, a large application might contain thousands of lines in a single file.
This can make the code:
- Difficult to read
- Difficult to test
- Difficult to debug
- Difficult to reuse
- Difficult for multiple developers to maintain
With modules, related functionality can be separated into different files.
For example:
1my_project/ 2│ 3├── main.py 4├── calculator.py 5├── users.py 6├── database.py 7└── utilities.py
Each file has a specific responsibility.
This approach improves:
- Code organization
- Reusability
- Maintainability
- Testing
- Collaboration
- Debugging
What Is a Package?
A package is a directory used to organize related Python modules.
For example:
1project/ 2│ 3├── main.py 4│ 5└── utilities/ 6 ├── __init__.py 7 ├── calculator.py 8 └── converter.py
Here:
projectis the project directoryutilitiesis a packagecalculator.pyis a moduleconverter.pyis another module__init__.pymarks and configures the package
Modern Python can recognize regular directories as packages through namespace packages, so __init__.py is not required in every package. However, it is still commonly used when you want explicit package initialization or a traditional package structure.
Creating a Simple Package
Suppose your project looks like this:
1shop_app/ 2│ 3├── main.py 4│ 5└── products/ 6 ├── __init__.py 7 ├── pricing.py 8 └── inventory.py
The pricing.py module could contain:
1def calculate_discount(price, percentage): 2 return price - (price * percentage / 100)
The inventory.py module could contain:
1def check_stock(quantity): 2 return quantity > 0
Now main.py can import them:
1from products.pricing import calculate_discount 2from products.inventory import check_stock 3 4price = calculate_discount(1000, 10) 5 6print("Final price:", price) 7print("In stock:", check_stock(5))
Output:
1Final price: 900.0 2In stock: True
The dot notation represents the package structure:
1products.pricing
means:
1products package → pricing module
Python Standard Library
Python comes with a large collection of modules called the Python Standard Library.
These modules are available with Python and generally do not require installation with pip.
Some commonly used modules are:
| Module | Purpose |
|---|---|
math | Mathematical operations |
random | Random values and selections |
datetime | Dates and times |
pathlib | Working with filesystem paths |
os | Operating-system functionality |
sys | Python runtime and command-line information |
statistics | Statistical calculations |
json | JSON data processing |
csv | CSV file processing |
re | Regular expressions |
string | Common string constants |
collections | Specialized collection types |
Learning the Standard Library can significantly reduce the amount of code you need to write yourself.
Using the import Statement
The simplest way to import a module is:
1import module_name
For example:
1import math 2 3print(math.sqrt(25))
Output:
15.0
The module name is followed by a dot when accessing something inside the module:
1math.sqrt() 2math.pi 3math.factorial()
This makes it clear where the functionality comes from.
Importing Multiple Modules
You can import multiple modules:
1import math 2import random 3 4print(math.pi) 5print(random.randint(1, 10))
However, it is generally better to keep imports clear and organized.
For example:
1import math 2import random
is easier to read than placing unrelated imports throughout the program.
Using from ... import
You can import a specific function, class, or constant from a module.
Syntax:
1from module_name import item
Example:
1from math import sqrt 2 3print(sqrt(49))
Output:
17.0
You no longer need to write:
1math.sqrt(49)
Instead, you can directly call:
1sqrt(49)
Importing Multiple Items
You can import multiple functions or constants:
1from math import sqrt, factorial 2 3print(sqrt(64)) 4print(factorial(5))
Output:
18.0 2120
Only import the functionality you actually need when this improves readability.
Using the as Keyword
The as keyword creates an alias.
Syntax:
1import module_name as alias
Example:
1import math as m 2 3print(m.sqrt(81)) 4print(m.pi)
Output:
19.0 23.141592653589793
You can also create aliases for imported functions:
1from math import factorial as fact 2 3print(fact(6))
Output:
1720
Aliases are useful when a module has a long name or when a conventional short name improves readability.
Avoid from module import *
Python allows this:
1from math import *
You could then write:
1print(sqrt(16)) 2print(pi)
Although it works, this style is generally discouraged.
The problem is that it imports many names into the current namespace, which can cause naming conflicts and make the source of a function difficult to identify.
Prefer:
1import math 2 3print(math.sqrt(16))
or:
1from math import sqrt 2 3print(sqrt(16))
The math Module
The math module provides commonly used mathematical functions and constants.
1import math
Square Root
1import math 2 3number = 36 4 5print(math.sqrt(number))
Output:
16.0
Power
1import math 2 3print(math.pow(2, 5))
Output:
132.0
For integer exponentiation, Python's ** operator is often simpler:
1print(2 ** 5)
Output:
132
Mathematical Constants
1import math 2 3print("Pi:", math.pi) 4print("Euler's number:", math.e)
Floor and Ceiling
floor() rounds downward, while ceil() rounds upward.
1import math 2 3print(math.floor(5.9)) 4print(math.ceil(5.1))
Output:
15 26
Factorial
1import math 2 3print(math.factorial(5))
Output:
1120
Trigonometric Functions
Python's trigonometric functions use radians.
Convert degrees to radians with math.radians():
1import math 2 3angle = math.radians(90) 4 5print(math.sin(angle))
Output:
11.0
The random Module
The random module provides pseudo-random number generation and random selections.
1import random
Generate a Random Integer
1import random 2 3number = random.randint(1, 10) 4 5print(number)
randint(1, 10) includes both 1 and 10.
Generate a Random Float
1import random 2 3number = random.random() 4 5print(number)
random() returns a floating-point number from 0.0 up to, but not including, 1.0.
Select a Random Item
1import random 2 3colors = ["Red", "Blue", "Green", "Yellow"] 4 5selected_color = random.choice(colors) 6 7print(selected_color)
Shuffle a List
1import random 2 3cards = [1, 2, 3, 4, 5] 4 5random.shuffle(cards) 6 7print(cards)
shuffle() changes the list in place.
Important: random Is Not for Password Security
The random module is useful for games, simulations, testing, and other non-security applications.
Do not use it to generate security-sensitive passwords, authentication tokens, reset tokens, or cryptographic secrets.
For security-sensitive random values, Python provides the secrets module.
Example:
1import secrets 2import string 3 4characters = string.ascii_letters + string.digits 5 6token = "".join( 7 secrets.choice(characters) 8 for _ in range(16) 9) 10 11print(token)
This is a better choice for security-sensitive random strings.
The datetime Module
The datetime module provides classes for working with dates and times.
1from datetime import date, datetime
Current Date
1from datetime import date 2 3today = date.today() 4 5print(today)
Example output:
12026-09-02
Current Date and Time
1from datetime import datetime 2 3now = datetime.now() 4 5print(now)
Create a Specific Date
1from datetime import date 2 3birthday = date(2003, 5, 18) 4 5print(birthday)
Format a Date
Use strftime() to format dates:
1from datetime import datetime 2 3now = datetime.now() 4 5formatted_date = now.strftime("%d-%m-%Y") 6 7print(formatted_date)
Example output:
102-09-2026
Calculate the Difference Between Dates
1from datetime import date 2 3today = date.today() 4new_year = date(today.year + 1, 1, 1) 5 6remaining = new_year - today 7 8print("Days remaining:", remaining.days)
This returns the number of days between the two dates.
The pathlib Module
For modern Python applications, pathlib is often a convenient choice for filesystem paths.
Instead of manually constructing path strings, you can use Path.
1from pathlib import Path 2 3current_directory = Path.cwd() 4 5print(current_directory)
List Files
1from pathlib import Path 2 3for path in Path.cwd().iterdir(): 4 print(path)
Check Whether a File Exists
1from pathlib import Path 2 3file_path = Path("data.txt") 4 5if file_path.exists(): 6 print("File exists") 7else: 8 print("File does not exist")
Create a Directory
1from pathlib import Path 2 3folder = Path("reports") 4 5folder.mkdir(exist_ok=True) 6 7print("Directory ready")
exist_ok=True prevents an error if the directory already exists.
The os Module
The os module provides operating-system-related functionality.
1import os
Current Working Directory
1import os 2 3print(os.getcwd())
List Directory Contents
1import os 2 3for item in os.listdir(): 4 print(item)
Create a Directory
1import os 2 3os.makedirs("reports", exist_ok=True) 4 5print("Directory created")
Rename a File
1import os 2 3if os.path.exists("old.txt"): 4 os.rename("old.txt", "new.txt")
Remove a File
1import os 2 3if os.path.exists("data.txt"): 4 os.remove("data.txt")
For many new filesystem-related programs, pathlib provides a cleaner object-oriented interface.
The sys Module
The sys module provides access to Python interpreter and runtime information.
1import sys
Check Python Version
1import sys 2 3print(sys.version)
For a more structured version value:
1import sys 2 3print(sys.version_info)
Command-Line Arguments
sys.argv contains command-line arguments passed to the Python program.
Create:
1app.py
1import sys 2 3print(sys.argv)
Run:
1python app.py hello world
You might see:
1['app.py', 'hello', 'world']
The first item is normally the script name.
Exit a Program
1import sys 2 3print("Program started") 4 5sys.exit() 6 7print("This line will not execute")
The statistics Module
The statistics module provides common statistical calculations.
1import statistics
Mean
1import statistics 2 3marks = [80, 90, 95, 88] 4 5print(statistics.mean(marks))
Output:
188.25
Median
1import statistics 2 3numbers = [10, 20, 30, 40, 50] 4 5print(statistics.median(numbers))
Output:
130
Mode
1import statistics 2 3numbers = [1, 2, 2, 3, 4] 4 5print(statistics.mode(numbers))
Output:
12
Standard Deviation
1import statistics 2 3numbers = [10, 20, 30, 40] 4 5print(statistics.stdev(numbers))
Creating Your Own Module
One of the most important skills is creating reusable modules yourself.
Create:
1calculator.py
1def add(a, b): 2 return a + b 3 4 5def subtract(a, b): 6 return a - b 7 8 9def multiply(a, b): 10 return a * b 11 12 13def divide(a, b): 14 if b == 0: 15 raise ValueError("Cannot divide by zero") 16 17 return a / b
Now create:
1main.py
1import calculator 2 3print(calculator.add(10, 5)) 4print(calculator.subtract(10, 5)) 5print(calculator.multiply(10, 5)) 6print(calculator.divide(10, 5))
Output:
115 25 350 42.0
This is much better than copying the same calculator functions into every Python file that needs them.
Using if __name__ == "__main__":
A very important Python pattern is:
1if __name__ == "__main__": 2 ...
Consider this module:
1def greet(name): 2 return f"Hello, {name}!" 3 4 5print(greet("Ankit"))
If another file imports this module:
1import greeting
the print() statement also executes during import.
To prevent this, write:
1def greet(name): 2 return f"Hello, {name}!" 3 4 5if __name__ == "__main__": 6 print(greet("Ankit"))
Now:
1python greeting.py
runs the example code.
But:
1import greeting
only imports the function without executing the example code.
This pattern is extremely common in Python applications and libraries.
Importing From a Package
Consider:
1project/ 2│ 3├── main.py 4│ 5└── utilities/ 6 ├── __init__.py 7 ├── calculator.py 8 └── temperature.py
calculator.py:
1def add(a, b): 2 return a + b
temperature.py:
1def celsius_to_fahrenheit(celsius): 2 return (celsius * 9 / 5) + 32
You can import specific functions in main.py:
1from utilities.calculator import add 2from utilities.temperature import celsius_to_fahrenheit 3 4print(add(10, 20)) 5print(celsius_to_fahrenheit(25))
Output:
130 277.0
Importing a Module From a Package
Instead of importing individual functions:
1from utilities import calculator 2 3print(calculator.add(10, 20))
Or:
1from utilities import temperature 2 3print(temperature.celsius_to_fahrenheit(25))
This style makes it clear which module provides the functionality.
Understanding __init__.py
A package may contain an __init__.py file:
1utilities/ 2├── __init__.py 3├── calculator.py 4└── temperature.py
The file can be empty:
1undefined
Or it can contain package-level initialization code.
For example:
1from .calculator import add
Then code can potentially import:
1from utilities import add 2 3print(add(5, 10))
Use package initialization deliberately. Avoid putting expensive or surprising side effects in __init__.py.
Relative Imports
Inside a package, modules can import related modules using relative imports.
Example:
1app/ 2│ 3└── services/ 4 ├── __init__.py 5 ├── user.py 6 └── email.py
Inside user.py:
1from .email import send_email
The . means the current package.
Relative imports are useful when organizing larger Python applications into multiple packages and modules.
Practice Project 1: Dice Roller
Let's create a simple dice roller using the random module.
1import random 2 3print("Dice Roller") 4 5roll = random.randint(1, 6) 6 7print("You rolled:", roll)
Example output:
1Dice Roller 2You rolled: 4
Because the result is random, your output may be different.
Roll Multiple Times
1import random 2 3try: 4 times = int(input("How many times should the dice roll? ")) 5 6 if times <= 0: 7 raise ValueError("Number of rolls must be greater than zero") 8 9 for roll_number in range(1, times + 1): 10 result = random.randint(1, 6) 11 print(f"Roll {roll_number}: {result}") 12 13except ValueError as error: 14 print("Invalid input:", error)
Practice Project 2: Secure Password Generator
For a password generator, use Python's secrets module rather than random when the generated password is intended for security-sensitive use.
1import secrets 2import string 3 4 5def generate_password(length): 6 if length < 8: 7 raise ValueError("Password length must be at least 8") 8 9 characters = string.ascii_letters + string.digits + string.punctuation 10 11 return "".join( 12 secrets.choice(characters) 13 for _ in range(length) 14 ) 15 16 17try: 18 length = int(input("Password length: ")) 19 20 password = generate_password(length) 21 22 print("Generated password:") 23 print(password) 24 25except ValueError as error: 26 print("Error:", error)
This example also demonstrates how a module can be combined with a reusable function.
Practice Project 3: Temperature Converter Module
Create:
1temperature.py
1def celsius_to_fahrenheit(celsius): 2 return (celsius * 9 / 5) + 32 3 4 5def fahrenheit_to_celsius(fahrenheit): 6 return (fahrenheit - 32) * 5 / 9
Now create:
1main.py
1from temperature import ( 2 celsius_to_fahrenheit, 3 fahrenheit_to_celsius, 4) 5 6print(celsius_to_fahrenheit(25)) 7print(fahrenheit_to_celsius(77))
Output:
177.0 225.0
The conversion logic is now reusable across multiple programs.
Practice Project 4: Student Statistics
Use the statistics module to analyze student marks.
1import statistics 2 3marks = [78, 85, 92, 88, 95] 4 5average = statistics.mean(marks) 6median = statistics.median(marks) 7 8print("Marks:", marks) 9print("Average:", average) 10print("Median:", median)
Output:
1Marks: [78, 85, 92, 88, 95] 2Average: 87.6 3Median: 88
Practice Project 5: Days Until New Year
1from datetime import date 2 3today = date.today() 4new_year = date(today.year + 1, 1, 1) 5 6remaining_days = (new_year - today).days 7 8print("Days until New Year:", remaining_days)
This example demonstrates how the datetime module can be used for date calculations.
A Practical Project Structure
As your project grows, you can organize it into packages.
For example:
1student_management/ 2│ 3├── main.py 4│ 5├── students/ 6│ ├── __init__.py 7│ ├── models.py 8│ └── services.py 9│ 10├── utils/ 11│ ├── __init__.py 12│ └── validators.py 13│ 14└── reports/ 15 ├── __init__.py 16 └── statistics.py
Each package has a specific responsibility.
For example:
1students/models.py
can contain student-related classes.
1students/services.py
can contain student operations.
1utils/validators.py
can contain reusable validation functions.
1reports/statistics.py
can contain reporting and statistical functionality.
This type of organization becomes increasingly useful in medium and large Python applications.
Module vs Package
| Feature | Module | Package |
|---|---|---|
| Meaning | A Python file | A directory containing related modules |
| Typical extension | .py | Directory |
| Purpose | Reuse code | Organize related modules |
| Example | calculator.py | utilities/ |
| Can contain functions | Yes | Through modules |
| Can contain classes | Yes | Through modules |
| Used in large projects | Yes | Yes |
A simple way to remember the difference is:
1Module → one Python file 2Package → collection/organization of modules
Common Import Errors
Forgetting to Import a Module
Incorrect:
1print(math.sqrt(25))
Possible error:
1NameError: name 'math' is not defined
Correct:
1import math 2 3print(math.sqrt(25))
Incorrect Module Name
Incorrect:
1import Maths
Python module names are case-sensitive in many environments.
Correct:
1import math
If the module cannot be found, you may see:
1ModuleNotFoundError
Importing a Function Incorrectly
Suppose calculator.py contains:
1def add(a, b): 2 return a + b
This is incorrect:
1from calculator import addition
because the function is named add, not addition.
Correct:
1from calculator import add 2 3print(add(5, 10))
Circular Imports
A circular import can happen when two modules import each other.
For example:
1module_a.py → imports module_b 2module_b.py → imports module_a
This can cause confusing import errors.
A better solution is often to move shared functionality into a third module.
For example:
1module_a.py 2module_b.py 3common.py
Then both modules can import from:
1from common import some_function
Import Search Path
When Python imports a module, it searches locations listed in sys.path.
You can inspect them with:
1import sys 2 3for path in sys.path: 4 print(path)
This can help diagnose situations where Python cannot find your module.
Avoid modifying sys.path as a first solution. A properly structured project and correct package installation/imports are usually better approaches.
Modules and Code Reusability
One of the biggest advantages of modules is that the same functionality can be reused in multiple programs.
For example:
1from calculator import add
can be used in:
1calculator_app.py 2billing.py 3student_system.py 4report.py
without duplicating the implementation.
This follows an important software development principle:
Write reusable functionality once and use it wherever it is needed.
Best Practices for Python Modules
Keep Modules Focused
A module should generally have a clear responsibility.
Good:
1database.py 2email.py 3calculator.py
Less maintainable:
1everything.py
containing unrelated functionality.
Use Descriptive Names
Prefer:
1calculator.py 2database.py 3validators.py
instead of:
1x.py 2abc.py 3test123.py
Keep Imports Organized
Place imports near the beginning of a module:
1import math 2import random 3 4from datetime import date 5 6from mypackage.utils import validate_email
Avoid Wildcard Imports
Prefer:
1import math 2 3math.sqrt(25)
over:
1from math import *
Protect Executable Example Code
Use:
1if __name__ == "__main__": 2 main()
when a module can both be imported and executed directly.
Exercises
Exercise 1: Circle Calculator
Create a module named circle.py with functions to calculate:
- Area
- Circumference
Use the math module.
Exercise 2: Random Team Selector
Create a program that randomly selects a student from:
1students = [ 2 "Ankit", 3 "Rahul", 4 "Aman", 5 "Priya", 6]
Use the random module.
Exercise 3: Date Calculator
Ask the user for a date and calculate how many days remain until that date.
Use the datetime module.
Exercise 4: File Manager
Use pathlib to:
- Create a directory
- Check whether a file exists
- List files in the directory
Exercise 5: Statistics Calculator
Create a module named statistics_utils.py.
Add functions for:
1mean 2median 3maximum 4minimum
Then import these functions into main.py.
Exercise 6: Create a Python Package
Create this structure:
1calculator_project/ 2│ 3├── main.py 4│ 5└── calculator/ 6 ├── __init__.py 7 ├── arithmetic.py 8 └── advanced.py
Add basic arithmetic functions to arithmetic.py and advanced operations to advanced.py.
Import and use them from main.py.
Key Takeaways
After completing this module, you should understand:
- A module is a Python file containing reusable code.
- A package organizes related modules into directories.
importimports a module.from ... importimports specific objects.ascreates an alias.- Python provides a large Standard Library.
mathis useful for mathematical operations.randomis useful for non-security randomization.secretsshould be used for security-sensitive random values.datetimehandles dates and times.pathlibprovides a modern interface for filesystem paths.osprovides operating-system functionality.sysprovides access to Python runtime information.statisticsprovides common statistical calculations.- You can create your own reusable modules.
- Packages help organize larger applications.
if __name__ == "__main__":separates reusable module code from direct execution.- Avoid wildcard imports because they can make code harder to understand and maintain.
A strong understanding of modules and packages is essential before moving into larger Python applications, third-party libraries, virtual environments, and professional project structures.