1. What is a Module?
A **module** is simply a Python file (`.py` extension) that contains Python definitions and statements. Functions, classes, variables – anything you define in a `.py` file can be considered part of a module.
Why use modules?
-
Reusability: Write code once and use it in multiple other files.
-
Organization: Break down complex problems into smaller, manageable, logical units.
-
Namespace Management: Prevents naming conflicts between different parts of your program (e.g., if two different files define a function called `calculate_total`, they won't clash if accessed via their module names).
2. Importing Modules
To use code from one module in another, you use the `import` statement.
`import module_name`
Imports the entire module. You access its contents using `module_name.item_name`.
# File: my_utils.py
PI = 3.14159
def circle_area(radius):
return PI * radius * radius
def greet_user(name):
return f"Hello, {name}! Welcome to UdaanPath."
# File: main_app.py
import my_utils
radius = 5
area = my_utils.circle_area(radius)
print(f"Area of circle with radius {radius}: {area}")
message = my_utils.greet_user("Students")
print(message)
print(f"Value of PI from my_utils: {my_utils.PI}")
$ # Assuming my_utils.py and main_app.py are in the same directory
$ python main_app.py
Area of circle with radius 5: 78.53975
Hello, Students! Welcome to UdaanPath.
Value of PI from my_utils: 3.14159
`import module_name as alias`
Gives the module a shorter alias for convenience. Common for standard libraries (`import numpy as np`).
# File: main_app_alias.py
import my_utils as mu # Give my_utils an alias 'mu'
radius = 7
area = mu.circle_area(radius)
print(f"Area via alias: {area}")
$ python main_app_alias.py
Area via alias: 153.93791
`from module_name import item1, item2`
Imports specific items (functions, variables, classes) directly into the current namespace. You can then use them without the `module_name.` prefix.
# File: main_app_direct.py
from my_utils import circle_area, greet_user, PI
radius = 10
area = circle_area(radius) # No my_utils. prefix needed
print(f"Direct import area: {area}")
message = greet_user("Developers")
print(message)
print(f"Direct import PI: {PI}")
$ python main_app_direct.py
Direct import area: 314.159
Hello, Developers! Welcome to UdaanPath.
Direct import PI: 3.14159
Avoid `from module_name import *`: This imports *all* public names from a module into the current namespace. While convenient for interactive sessions, it makes it hard to tell where a function or variable came from and can lead to name clashes, especially in larger projects. It's generally considered bad practice for production code.
Module Search Path (`sys.path`)
When you import a module, Python searches for it in a list of directories. This list is available in `sys.path`. The order of search is typically:
- The directory containing the input script (or the current directory if in interactive mode).
- Directories in the `PYTHONPATH` environment variable.
- Standard library directories.
- The site-packages directory (where third-party libraries are installed).
3. The `if __name__ == "__main__":` Block
Every Python module has a special built-in variable called `__name__`.
-
If you run a Python file directly, its `__name__` is set to `"__main__"`.
-
If you import a Python file as a module into another file, its `__name__` is set to the module's name (e.g., `"my_utils"`).
This allows you to write code that only executes when the script is run directly, often used for testing, examples, or command-line interfaces.
# File: my_utility_module.py
def calculate_sum(a, b):
return a + b
print(f"__name__ in my_utility_module.py: {__name__}")
if __name__ == "__main__":
print("This code runs only when my_utility_module.py is executed directly.")
result = calculate_sum(10, 20)
print(f"Directly run sum: {result}")
# File: another_script.py
import my_utility_module
print(f"__name__ in another_script.py: {__name__}")
result_imported = my_utility_module.calculate_sum(5, 7)
print(f"Imported sum: {result_imported}")
$ python my_utility_module.py
__name__ in my_utility_module.py: __main__
This code runs only when my_utility_module.py is executed directly.
Directly run sum: 30
$ python another_script.py
__name__ in my_utility_module.py: my_utility_module
__name__ in another_script.py: __main__
Imported sum: 12
4. What is a Package?
A **package** is a way of organizing related modules into a directory hierarchy. Essentially, it's a folder containing one or more Python module files, and importantly, it *must* contain a special file named `__init__.py`.
-
The `__init__.py` file tells Python that the directory should be treated as a package. It can be an empty file, or it can contain initialization code for the package (e.g., defining what gets imported when `import package_name` is used).
-
Packages help you structure large projects logically, group related functionalities, and prevent naming conflicts at a larger scale.
5. Importing from Packages
Let's consider a typical UdaanPath project structure:
my_udaan_project/
├── main.py
└── data_processor/
├── __init__.py
├── cleaner.py
└── analyzer.py
File: `my_udaan_project/data_processor/__init__.py` (can be empty)
# This file can be empty for Python 3.3+ to recognize the directory as a package.
# Older Python versions required it to exist.
# You can also use it to define what happens when the package is imported:
# __all__ = ["cleaner", "analyzer"] # Defines what 'from data_processor import *' does
File: `my_udaan_project/data_processor/cleaner.py`
def clean_text(text):
"""Removes non-alphanumeric characters and converts to lowercase."""
return ''.join(char for char in text if char.isalnum()).lower()
def remove_duplicates(data_list):
"""Removes duplicate items from a list."""
return list(set(data_list))
File: `my_udaan_project/data_processor/analyzer.py`
def calculate_average(numbers):
"""Calculates the average of a list of numbers."""
if not numbers:
return 0
return sum(numbers) / len(numbers)
def count_occurrences(items):
"""Counts occurrences of each item in a list."""
counts = {}
for item in items:
counts[item] = counts.get(item, 0) + 1
return counts
Absolute Imports (from `main.py`):
# File: my_udaan_project/main.py
# Absolute imports: specify full path from the project root
from data_processor.cleaner import clean_text, remove_duplicates
from data_processor.analyzer import calculate_average
# Simulate some raw data
raw_sensor_data = ["Temp: 25C", "Humidity: 60%", "Temp: 25C", "Pressure: 1012hPa"]
numeric_data = [10, 20, 30, 10, 40, 50]
# Use functions from imported modules
cleaned_temp = clean_text(raw_sensor_data[0])
print(f"Cleaned temp data: {cleaned_temp}")
unique_data = remove_duplicates(raw_sensor_data)
print(f"Unique sensor data: {unique_data}")
avg_value = calculate_average(numeric_data)
print(f"Average of numeric data: {avg_value}")
$ # Navigate to the 'my_udaan_project' directory first
$ cd my_udaan_project/
$ python main.py
Cleaned temp data: temp25c
Unique sensor data: ['Temp: 25C', 'Humidity: 60%', 'Pressure: 1012hPa']
Average of numeric data: 26.666666666666668
Relative Imports (within the `data_processor` package):
Modules within a package can import from each other using relative imports, denoted by dots (`.` for current package, `..` for parent package, etc.). This makes your code more portable within the package.
Modified File: `my_udaan_project/data_processor/analyzer.py` (to use `cleaner.py`)
# File: my_udaan_project/data_processor/analyzer.py
from .cleaner import clean_text # Relative import from the same package (cleaner.py)
def calculate_average(numbers):
"""Calculates the average of a list of numbers."""
if not numbers:
return 0
return sum(numbers) / len(numbers)
def count_occurrences(items):
"""Counts occurrences of each item in a list."""
counts = {}
for item in items:
# Example of using clean_text from cleaner module in analyzer
cleaned_item = clean_text(str(item)) # Ensure item is string for clean_text
counts[cleaned_item] = counts.get(cleaned_item, 0) + 1
return counts
$ # Now, if main.py calls count_occurrences, it will use the cleaned text logic:
$ python main.py # main.py is unchanged but now uses the updated analyzer.py
Cleaned temp data: temp25c
Unique sensor data: ['Temp: 25C', 'Humidity: 60%', 'Pressure: 1012hPa']
Average of numeric data: 26.666666666666668
# (The output won't explicitly show the change unless main.py calls count_occurrences)
6. Standard Library Modules
Python comes with a vast **Standard Library** – a collection of pre-built modules for common tasks. You've likely used some already! Examples include:
- `math`: Mathematical functions (e.g., `math.sqrt`, `math.pi`).
- `random`: Generating random numbers (e.g., `random.randint`).
- `datetime`: Working with dates and times.
- `os`: Interacting with the operating system (e.g., file paths, environment variables).
- `sys`: Accessing system-specific parameters and functions (e.g., `sys.path`).
Always check the Python Standard Library documentation before writing your own code for common functionalities – chances are, it's already implemented and well-tested!