16.1 Introduction to Modules
A module is a Python file that contains reusable code such as variables, functions, and classes. Modules help you divide a large program into smaller and easier parts. Instead of writing everything in one file, you can place related code in separate files and import it whenever it is needed.
Example
import math
print(math.sqrt(25))
Output
5.0
Explanation: Python imports the built-in math module. The sqrt() function from that module calculates the square root of 25, so the displayed result is 5.0.
16.2 Creating Modules
You create a module by saving Python code in a file ending with .py. The file name becomes the module name. A module can contain functions, variables, or classes. Another Python file can then import that module and use its code without copying the code into the new file.
Example
# greetings.py
def say_hello(name):
return "Hello, " + name
# main.py
import greetings
print(greetings.say_hello("Mina"))
Output
Hello, Mina
Explanation: The greetings.py file defines say_hello(). The main.py file imports greetings and calls the function using the module name followed by a dot.
16.3 Importing Modules
Importing a module makes the code inside another Python file available to your current program. Python loads the module, creates its namespace, and lets you access its members. Imported modules reduce repeated code and make programs easier to test, update, and organize.
Example
import random
number = random.randint(1, 5)
print(number in [1, 2, 3, 4, 5])
Output
True
Explanation: randint(1, 5) always returns a number from 1 through 5. The membership test therefore displays True, although the exact random number can change each time.
16.4 The import Statement
The import statement loads an entire module. When you import the whole module, you normally use the module name before its functions or variables. This makes the source of each name clear and helps prevent conflicts between functions that happen to have the same name.
Example
import math
print(math.pi)
print(math.floor(4.9))
Output
3.141592653589793
4
Explanation: The math module provides pi and floor(). The module name is written before each item, showing clearly that both values come from math.
16.5 The from Statement
The from statement imports selected content from a module. After importing a name this way, you can use it directly without typing the module name first. This can make code shorter, but you should still choose names carefully so that imported names do not conflict with your own variables or functions.
Example
from math import sqrt
print(sqrt(81))
Output
9.0
Explanation: Only sqrt is imported from math. Because it is imported directly, the program calls sqrt(81) instead of math.sqrt(81).
16.6 Importing Specific Names
Python allows several specific names to be imported from one module in a single statement. This is useful when you need only a small part of a large module. Importing selected names can make code more focused, but the reader should still be able to understand where the names came from.
Example
from math import ceil, floor
print(ceil(3.2))
print(floor(3.8))
Output
4
3
Explanation: ceil() rounds upward to 4, while floor() rounds downward to 3. Both functions were imported directly from the math module.
16.7 Import Aliases
An alias gives an imported module or name a different local name. Aliases are commonly used to shorten long module names or avoid naming conflicts. The as keyword creates the alias. A good alias should be short, understandable, and consistent with common Python conventions.
Example
import math as m
print(m.pow(2, 3))
Output
8.0
Explanation: The math module is renamed locally as m. The program then calls m.pow(2, 3), which calculates two raised to the third power.
16.8 The dir() Function
The dir() function shows the names available inside an object or module. It is helpful when exploring a module and learning what functions, variables, and classes it provides. The result may include many special names that begin and end with double underscores, along with normal public names.
Example
import math
names = dir(math)
print("sqrt" in names)
print("pi" in names)
Output
True
True
Explanation: dir(math) returns a list of names in the math module. Both sqrt and pi are present, so both membership checks display True.
16.9 The __name__ Variable
Every Python module has a special variable named __name__. When a file runs directly, its __name__ value is "__main__". When the same file is imported, __name__ becomes the module's actual name. This allows a file to behave differently when run directly and when imported.
Example
print(__name__)
Output
__main__
Explanation: When this file is executed directly, Python assigns the special value __main__ to its __name__ variable.
16.10 if __name__ == "__main__"
This condition checks whether a Python file is being run directly. Code inside the block runs only when the file is the main program. It does not run automatically when the file is imported. This pattern is useful for demonstrations, tests, command-line actions, and program starting points.
Example
def greet():
print("Welcome!")
if __name__ == "__main__":
greet()
Output
Welcome!
Explanation: Because the file runs directly, __name__ equals __main__. The condition is True, so greet() is called and prints the message.
16.11 Module Search Paths
When Python imports a module, it searches several locations in a specific order. It normally checks the current project folder, installed package locations, and standard library folders. If Python cannot find the requested module in any search location, it raises a ModuleNotFoundError.
Example
import os
print(os.path.basename("/home/student/app.py"))
Output
app.py
Explanation: Python finds the standard-library os module through its search path. basename() then returns only the file name from the complete path.
16.12 The sys.path List
The sys.path list contains the folders Python checks when importing modules. You can inspect this list to understand where Python looks for code. Although it is possible to add folders to sys.path while a program runs, properly organized packages and environments are usually a cleaner solution.
Example
import sys
print(isinstance(sys.path, list))
print(len(sys.path) > 0)
Output
True
True
Explanation: sys.path is a list, and it normally contains one or more search locations. Therefore, both checks display True.
16.13 Reloading Modules
Python normally imports a module only once during a program session. If the module file changes while the program is still running, importlib.reload() can load the updated version again. Reloading is mainly useful during interactive development and testing, not as a normal replacement for restarting a program.
Example
import math
import importlib
importlib.reload(math)
print(math.factorial(5))
Output
120
Explanation: reload() loads the math module again. factorial(5) then multiplies 5 × 4 × 3 × 2 × 1 and returns 120.
16.14 Introduction to Packages
A package is a folder that groups related Python modules. Packages make it easier to organize larger programs by subject or feature. For example, a shopping application might have separate packages for products, customers, payments, and reports. Packages can also contain smaller subpackages.
Example
shop/
├── __init__.py
├── products.py
└── payments.py
Output
A package named shop containing two modules.
Explanation: The folder groups related modules together. Python code can import products or payments through the shop package name.
16.15 Creating Packages
To create a traditional package, make a folder for the package and place Python module files inside it. Adding an __init__.py file clearly marks the folder as a package and can also run package initialization code. The project should be started from a location where Python can find the package.
Example
# tools/calculator.py
def add(a, b):
return a + b
# main.py
from tools.calculator import add
print(add(4, 6))
Output
10
Explanation: calculator.py is inside the tools package. The main file imports add from that package and displays the sum of 4 and 6.
16.16 __init__.py
The __init__.py file is used when a package is imported. It can be empty, define package-level variables, or expose selected functions from internal modules. Keeping it simple is often best for beginners. It helps users access important package features through a shorter and cleaner import path.
Example
# tools/__init__.py
from .calculator import add
# main.py
from tools import add
print(add(2, 7))
Output
9
Explanation: __init__.py exposes add at the package level. The main file can therefore import add directly from tools.
16.17 Subpackages
A subpackage is a package placed inside another package. Subpackages provide an extra level of organization for large applications. A store package might contain separate inventory, orders, and users subpackages. Each subpackage can contain its own modules and, when needed, its own __init__.py file.
Example
store/
├── __init__.py
└── orders/
├── __init__.py
└── receipt.py
Output
The orders package is nested inside the store package.
Explanation: The folder structure creates store.orders as a subpackage path. Modules inside receipt.py can be imported through that full path.
16.18 Absolute Imports
An absolute import uses the complete package path starting from the project’s top-level package. Absolute imports are clear because they show exactly where the imported item comes from. They are often preferred in large projects because the import remains easy to understand from different modules.
Example
from store.orders.receipt import create_receipt
print(create_receipt("Book"))
Output
Receipt created for Book
Explanation: The import states the complete route from store to orders to receipt. The imported function then creates the displayed message.
16.19 Relative Imports
A relative import locates another module based on the current package. One dot means the current package, while two dots refer to the parent package. Relative imports are useful for internal package code, but they should be used carefully because too many dots can make imports harder to read.
Example
# store/orders/receipt.py
from .tax import calculate_tax
print(calculate_tax(100))
Output
13.0
Explanation: The single dot tells Python to find tax.py in the same orders package. calculate_tax() returns 13 percent of 100.
16.20 Namespace Packages
A namespace package allows one logical package to be spread across multiple folders. Unlike a traditional package, it may not require an __init__.py file. Namespace packages are useful for large libraries, plugins, or organizations that distribute different parts of the same package separately.
Example
folder_one/company/reports.py
folder_two/company/accounts.py
Output
Both locations can contribute modules to the company namespace.
Explanation: Python can combine matching namespace folders from different search locations so they behave like parts of one package.
16.21 Avoiding Circular Imports
A circular import happens when two modules import each other, directly or indirectly. This can leave one module only partly initialized and cause errors. You can often avoid the problem by moving shared code into a third module, importing inside a function, or redesigning responsibilities more clearly.
Example
# shared.py
def format_name(name):
return name.title()
# users.py and reports.py can both import shared.py
Output
Both modules reuse shared code without importing each other.
Explanation: Moving the common function into shared.py breaks the circular dependency and gives both modules one safe place to import from.
16.22 Organizing Large Projects
Large Python projects should separate source code, tests, configuration, documentation, and data into clear folders. Related features should be grouped into packages and modules with descriptive names. A clean structure helps developers understand the project, avoid duplicated code, run tests, and maintain the application over time.
Example
my_project/
├── app/
│ ├── __init__.py
│ ├── users.py
│ └── orders.py
├── tests/
├── README.md
└── main.py
Output
A clear project with application code, tests, documentation, and an entry file.
Explanation: Each folder has one main purpose. This makes files easier to locate and keeps the project understandable as it grows.
16.23 Chapter Practice Exercises
Practice helps you remember how modules and packages work. Create small files, import functions in different ways, use aliases, inspect modules with dir(), and build a simple package. Try changing file locations and reading errors carefully so you understand how Python finds imported code.
Example
# converter.py
def km_to_miles(km):
return km * 0.621371
# main.py
from converter import km_to_miles
print(round(km_to_miles(10), 2))
Output
6.21
Explanation: The exercise creates a reusable conversion function in one module and imports it into another file. Ten kilometres converts to about 6.21 miles.
16.24 Chapter Mini Project
In this mini project, you create a small utility package with separate modules for calculations and messages. The main program imports both parts and combines them. This project demonstrates practical file organization, package imports, reusable functions, and a clear starting point for the application.
Example
# utilities/math_tools.py
def total(price, quantity):
return price * quantity
# utilities/messages.py
def receipt_message(amount):
return f"Your total is ${amount:.2f}"
# main.py
from utilities.math_tools import total
from utilities.messages import receipt_message
amount = total(12.5, 3)
print(receipt_message(amount))
Output
Your total is $37.50
Explanation: The math module calculates 12.5 multiplied by 3. The messages module formats the result as a receipt sentence, and main.py displays the final total.