
Python Modules¶
Introduction to Python Modules¶
In Python, a module is a file containing Python definitions and statements organized for reuse. Modules allow you to segment your code into logical components, promoting readability and maintainability.
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.
Creating a Module¶
- To create a module, simply save your Python code in a file with the
.py
extension.
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
import
statement to access functions, classes, and variables defined in another file.
Syntax:
import module_name
module_name.member_name
# 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!
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
as
keyword 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 current namespace |
# main.py
from intensity_utils import greet_user
greet_user("Carol") # Output: Welcome to Intensity Coding, Carol!
Welcome to Intensity Coding, Carol!
Exploring Module Contents¶
dir()
: Lists all defined names 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
PYTHONPATH
environment 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.path
to view or extend search paths:
import sys
print(sys.path)
# You may append custom paths:
sys.path.append('/path/to/your/modules')
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__.py
inside thetools
directory 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
tools
package 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
- Although
_log_email()
exists in the file, it won't be imported with*
due to the underscore convention. However, you could still import it directly usingfrom mail_utils import _log_email
, which is 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.
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 .core import * # Import everything 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