Introduction to Python Modules¶
In Python, a module is a file containing Python definitions and statements organized for reuse.
Each Python file is considered a module if it has the
.pyextension. For example, a file named Test.py is a module, where Test is the module name.Modules help organize large codebases into smaller, manageable, and reusable components. Using modules also allows code sharing across different programs.
Key features of modules:
- Encapsulation: Group related functions, classes, and variables.
- Reusability: Import once and use across multiple scripts.
- Namespace Management: Avoid name collisions through module scoping.
Types of Modules¶
Python supports two main types of modules:
- Built-in Modules
- User-defined Modules
Built-in Modules¶
Built-in modules come pre-installed with Python.
They provide a wide range of reusable functionality, which makes Python a powerful and versatile language.
These modules cover areas like mathematics, file operations, date and time, random number generation, and more.
Some commonly used built-in modules include:
- math – mathematical functions
- os – interacting with the operating system
- sys – system-specific parameters and functions
- datetime – date and time operations
- random – generating random numbers
Example: Using the math module
import math
# Using math module to calculate square root
print(math.sqrt(25)) # Output: 5.0
5.0
User-defined Modules¶
- A user-defined module is a Python file created by the user to store functions, classes, or variables that can be reused in multiple programs.
Creating a Module¶
- To create a module, simply save your Python code in a file with the
.pyextension.
Example: Save the following in intensity_utils.py:
# intensity_utils.py
def greet_user(name):
"""Display a personalized greeting."""
print(f"Welcome to Intensity Coding, {name}!")
Importing a Module¶
- Use the
importstatement to access functions, classes, and variables defined in another file.
Syntax:
import module_name
module_name.member_name
Example
# main.py
import intensity_utils # Intensity Coding helper module
intensity_utils.greet_user("Alice") # Output: Welcome to Intensity Coding, Alice!
Welcome to Intensity Coding, Alice!
- Modules can be imported in several ways:
1. Import a Whole Module
import math
print(math.sqrt(16)) # Output: 4.0
4.0
2. Import Multiple Modules
import math, random
# Using math module
print(math.factorial(5)) # Output: 120
# Using random module
print(random.randint(10, 20)) # Output: (random number between 10 and 20)
120 13
3. Import Specific Functions or Classes
from math import sqrt, factorial
print(sqrt(49)) # Output: 7.0
print(factorial(6)) # Output: 720
7.0 720
Module Components: Variables and Data Structures¶
- Modules can store variables of any type, such as lists, dictionaries, or custom objects.
Example: Add to intensity_utils.py:
# intensity_utils.py continued
CONFIG = {
"version": "1.0",
"author": "Intensity Coding Team"
}
# main.py
from intensity_utils import CONFIG
# Access configuration values
print(CONFIG["version"]) # Output: 1.0
1.0
Aliasing Modules¶
- To simplify lengthy module names, use the
askeyword to create an alias.
Syntax:
import module_name as alias
# main.py
import intensity_utils as iu
iu.greet_user("Bob") # Output: Welcome to Intensity Coding, Bob!
Welcome to Intensity Coding, Bob!
Selective Imports¶
- You can import specific members from a module to avoid long qualifiers.
| Statement | Effect |
|---|---|
from module import name |
Imports name into current namespace |
from module import * |
Imports all public names into the current namespace |
# main.py
from intensity_utils import greet_user
greet_user("Carol") # Output: Welcome to Intensity Coding, Carol!
Welcome to Intensity Coding, Carol!
Example : Import All Names from a Module
from math import *
print(sin(1)) # Output: 0.8414709848078965
0.8414709848078965
Exploring Module Contents¶
dir(): Lists all defined members in a module.help(): Shows documentation for a specific member.
import intensity_utils
print(dir(intensity_utils))
# Output: [..., 'greet_user', ...]
help(intensity_utils.greet_user)
# Output: Display a personalized greeting.
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'greet_user']
Help on function greet_user in module intensity_utils:
greet_user(name)
Display a personalized greeting.
Module Search Path¶
When you import a module, Python will search for the module file from the folders specified in the sys.path variable.
When you import a module, Python searches directories in this order:
- Current directory
- Directories in the
PYTHONPATHenvironment variable - Standard library directories (e.g.,
/usr/local/lib/python3.x)
-Python allows you to modify the module search path by changing, adding, and removing elements from the sys.path variable.
- Use
sys.pathto view or extend search paths:
import sys
print(sys.path)
# You may append custom paths:
sys.path.append('/path/to/your/modules')
- Appending to
sys.pathworks at runtime, but it affects only the current interpreter session. - For permanent paths, users should use PYTHONPATH environment variable.
Reloading Modules¶
- By default, Python loads each module only once per session. To incorporate changes without restarting the interpreter, use
importlib.reload().
# main.py
import intensity_utils
from importlib import reload
# Modify intensity_utils.py externally, then:
reload(intensity_utils)
<module 'intensity_utils' from '/content/intensity_utils.py'>
Understanding __name__ in Python¶
When exploring Python scripts, you've likely encountered the following construct:
if __name__ == '__main__'This line might seem puzzling at first glance. Let's break it down and understand why it's important, especially in modular programming.
What is __name__ in Python?¶
__name__is a special built-in variable in Python.- It is known as a "dunder" variable, short for double underscore (
__before and after the name). - Its value changes based on how the script is being used.
Behavior of __name__¶
- The value of
__name__is determined by Python at runtime:
| Script Usage | Value of __name__ |
|---|---|
Script is run directly (e.g., python my_script.py) |
'__main__' |
| Script is imported as a module | 'module_name' (i.e., the file name without .py) |
This conditional behavior allows a script to act as both:
- A standalone program, or
- A reusable module that can be imported without running its main logic immediately.
Why Use if __name__ == '__main__':?¶
This check is a Python convention that lets you control the execution flow.
It ensures that certain code only runs when the script is executed directly, not when it is imported.
Common use cases include:
- Running test code
- Executing the main function
- Isolating side effects from module behavior
Syntax¶
if __name__ == '__main__':
# Code block that runs only when script is executed directly
Example¶
# Filename: greeting.py
def say_hello():
print("Hello from Intensity Coding!")
# Only executes when this script is run directly, not on import
if __name__ == '__main__':
say_hello()
Hello from Intensity Coding!
Expected Behavior:¶
Running directly:
$ python greeting.py
Output:
Hello from Intensity Coding!
Importing into another script:
import greeting # __name__ in greeting.py will be 'greeting'
Output: (No output, unless
say_hello()is explicitly called)
Packages: Organizing Multiple Modules¶
- A package in Python is a structured directory that serves as a container for multiple related modules and nested packages.
- This organization supports modular development and code reuse in large-scale applications.
- For Python to recognize a directory as a package, it must include a special file called
__init__.py, which can be empty or contain initialization code for the package.
Example: Utility Tools Package¶
- Suppose you're building a utility package named
tools, which contains modules for different categories of helper functions:
tools/file_ops.py: Defines thefile_info()functiontools/string_ops.py: Defines thestring_summary()functiontools/math_ops.py: Defines thesimple_add()function
- Create a file
__init__.pyinside thetoolsdirectory to make its contents available upon package import:
# tools/__init__.py
from .file_ops import file_info
from .string_ops import string_summary
from .math_ops import simple_add
- Now you can use the
toolspackage in your main application as follows:
import tools
tools.file_info("data.txt") # Example: Output file details
tools.string_summary("Intensity Coding") # Example: Summary of input string
tools.simple_add(3, 4) # Output: 7
Private Functions in a Module¶
Why Make Functions Private?¶
Modular Python code benefits from hiding internal functionality. Keeping some functions private helps:
- Prevent misuse from outside the module.
- Avoid cluttering the module's public interface.
- Protect internal logic used only within the module.
Approach 1: Private Functions Using _ Prefix¶
- In Python, prefixing a function with an underscore is a convention indicating it is for internal use only.
File: mail_utils.py¶
# mail_utils.py
def send_email(recipient, content):
"""Public function to send email"""
print(f"[SEND] To: {recipient} | Message: {content}")
def _log_email(filename):
"""Internal helper to log sent email to a file"""
print(f"[LOG] Email log written to: {filename}")
File: main.py¶
# main.py
from mail_utils import * # Imports only public names by default
send_email("team@intensitycoding.com", "Your ML training is confirmed!") # ✅ Accessible
_log_email("log.txt") # ❌ NameError: _log_email is not defined
- By convention,
_log_email()is meant for internal use, but Python does not prevent its import. - Using
from module import *may include it if__all__is not defined, so direct imports likefrom mail_utils import _log_emailare still possible but discouraged."
Approach 2: Private Functions Using __all__¶
- The
__all__list defines which symbols are considered public when usingfrom module import *. Anything not listed will remain private—even if it doesn’t have a leading underscore. __all__only affectsfrom module import *. You can still import the function directly even if it’s not in__all__."
Updated mail_utils.py with __all__¶
# mail_utils.py
__all__ = ['send_email'] # Only expose this function when using import *
def send_email(recipient, content):
"""Public function to send email"""
print(f"[SEND] To: {recipient} | Message: {content}")
def log_email(filename):
"""Private function (not in __all__)"""
print(f"[LOG] Email log saved to: {filename}")
File: main.py¶
# main.py
from mail_utils import *
send_email("user@intensitycoding.com", "Welcome to Intensity Coding!") # ✅ Allowed
log_email("mail.log") # ❌ NameError: 'log_email' is not defined
- This method hides
log_email()even though its name does not begin with_.
Building a Package with Private Logic¶
- Let’s apply this to a package-based structure where you want to expose only select functionality at the package level.
Project Structure¶
intensity_package/
├── mail/
│ ├── __init__.py
│ └── core.py
└── main.py
core.py: Internal Logic¶
# mail/core.py
__all__ = ['send_email'] # Declare public API of the submodule
def send_email(recipient, content):
print(f"[SEND] Message to {recipient}: {content}")
def save_to_drafts(filename):
print(f"[DRAFT] Message saved to {filename}") # Private function
__init__.py: Package Interface¶
# mail/__init__.py
from . import core # import the module itself
from .core import * # import all names from core
__all__ = core.__all__ # Expose only public items declared in core.py
- This ensures only
send_email()is accessible when usingfrom mail import *orimport mail.
main.py: Package Usage¶
# main.py
import mail
mail.send_email("student@intensitycoding.com", "Class starts at 10 AM") # ✅ Accessible
# The following is not accessible:
# mail.save_to_drafts("notes.txt") # ❌ AttributeError