In the previous lesson, we learned what Python modules are and how the import statement allows us to reuse code from another file. Now we will create our own Python modules from scratch and understand how to structure them properly.
A custom Python module is simply a Python file with a .py extension that contains code we want to reuse. The code can include functions, variables, constants, classes, and other Python statements.
For example, create a file named:
calculator.py
Inside the file, write:
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiply(a, b):
return a * b
We have now created our first custom Python module.
The important thing to understand is that calculator.py does not need to be a complete application. It can simply provide reusable functionality to other Python programs.
Now create another file in the same folder:
main.py
Inside main.py, import the module:
import calculator
We can now use the functions:
result1 = calculator.add(10, 5)
result2 = calculator.subtract(10, 5)
result3 = calculator.multiply(10, 5)
print(result1)
print(result2)
print(result3)
The output will be:
15
5
50
This demonstrates the basic relationship between a module and the program using it:
calculator.py
↓
Reusable functions
↓
import calculator
↓
main.py
↓
Use the functions
Creating your own Python modules becomes useful when you have functionality that should be reused or logically separated from the main program.
For example, a Data Analytics project might contain functions for:
Instead of placing all these functions inside one large file, we can organize them into separate modules.
data_loader.py
validation.py
analytics.py
report.py
This creates a cleaner project structure.
Let’s create a module that is more relevant to Data Analytics.
Create:
analytics_tools.py
Add the following functions:
def calculate_total(values):
return sum(values)
def calculate_average(values):
if not values:
return 0
return sum(values) / len(values)
def find_maximum(values):
if not values:
return None
return max(values)
def find_minimum(values):
if not values:
return None
return min(values)
Now create main.py:
import analytics_tools
sales = [
10000,
15000,
12000,
18000,
20000
]
total = analytics_tools.calculate_total(
sales
)
average = analytics_tools.calculate_average(
sales
)
maximum = analytics_tools.find_maximum(
sales
)
minimum = analytics_tools.find_minimum(
sales
)
print("Total:", total)
print("Average:", average)
print("Maximum:", maximum)
print("Minimum:", minimum)
Here, the main program does not contain the actual calculation logic. The calculations are maintained inside analytics_tools.py.
This is the basic idea behind reusable Python modules.
A good module should generally have a clear purpose.
For example:
sales_analysis.py
can contain functions related to sales analysis.
Similarly:
student_analysis.py
can contain functions related to student performance analysis.
Avoid creating a single module containing completely unrelated functionality such as:
calculate_sales()
send_email()
draw_chart()
connect_database()
calculate_student_grade()
It is usually better to divide these responsibilities into logical modules as the project grows.
Suppose you have five different Python programs that need to calculate the average of a list.
Instead of writing:
def calculate_average(values):
return sum(values) / len(values)
in all five programs, create it once inside:
analytics_tools.py
Then import it whenever required.
This follows an important programming principle:
Don’t unnecessarily repeat the same code.
Reusable modules help reduce duplication and make future maintenance easier.
Choose module names that clearly describe their purpose.
Good examples include:
data_loader.py
sales_analysis.py
student_tools.py
file_manager.py
report_generator.py
validation.py
Try to avoid unclear names such as:
x.py
abc.py
stuff.py
Clear names make a Python project easier to understand.
For normal Python module names, lowercase names with underscores are commonly used for readability.
student_analysis.py
is easier to understand than:
StudentAnalysisModule.py
When your project contains many files, naming becomes increasingly important.
For a simple beginner project, keep your custom module and main Python file in the same folder.
my_project/
│
├── main.py
└── analytics_tools.py
Then main.py can normally use:
import analytics_tools
This is the easiest structure to practice module creation.
When developing a module, you may want to test whether its functions work correctly.
For example:
def calculate_average(values):
if not values:
return 0
return sum(values) / len(values)
if __name__ == "__main__":
test_data = [
10,
20,
30
]
print(
calculate_average(test_data)
)
The function is reusable, while the example test code is protected by:
if __name__ == "__main__":
This means the test code runs when the file is executed directly but does not automatically run when another program imports the module.
You can also document what your module does.
For example:
"""
Reusable functions for basic sales analysis.
"""
def calculate_total(values):
return sum(values)
def calculate_average(values):
if not values:
return 0
return sum(values) / len(values)
This first string is called a module docstring.
It describes the purpose of the module and can help developers understand what the file is designed to do.
.py file containing reusable code.if __name__ == "__main__": can be used for direct execution and module testing.Custom Python modules are useful because they allow us to keep reusable code in one place and use that code across different programs. A module is not limited to functions. It can also contain variables, constants, classes, and other Python objects.
In this part, we will learn how to organize these different elements inside a custom module and how to import them into another Python program.
Suppose we create a module called:
analytics_tools.py
We can place several related components inside it:
DEFAULT_TAX_RATE = 0.18
def calculate_total(values):
return sum(values)
def calculate_average(values):
if not values:
return 0
return sum(values) / len(values)
Here, the module contains:
DEFAULT_TAX_RATEcalculate_total()calculate_average()Another Python file can import these objects and use them.
Suppose we have:
analytics_tools.py
with:
def calculate_total(values):
return sum(values)
def calculate_average(values):
if not values:
return 0
return sum(values) / len(values)
We can import the complete module:
import analytics_tools
and use:
sales = [
10000,
15000,
20000
]
total = analytics_tools.calculate_total(
sales
)
average = analytics_tools.calculate_average(
sales
)
print(total)
print(average)
Alternatively, we can import only the functions we need:
from analytics_tools import (
calculate_total,
calculate_average
)
Now we can call them directly:
total = calculate_total(sales)
average = calculate_average(sales)
Both approaches are valid.
The first approach keeps the module name visible:
analytics_tools.calculate_total()
The second approach gives direct access to the function:
calculate_total()
One of the best uses of a custom module is creating reusable utility functions.
For example, create:
number_utils.py
Add:
def is_even(number):
return number % 2 == 0
def is_positive(number):
return number > 0
def calculate_percentage(value, total):
if total == 0:
return 0
return (
value / total
) * 100
Now another program can reuse these functions:
from number_utils import (
is_even,
is_positive,
calculate_percentage
)
print(
is_even(10)
)
print(
is_positive(20)
)
print(
calculate_percentage(
80,
100
)
)
This is much better than copying the same functions into every program.
A custom module can also contain variables.
For example:
course_config.py
COURSE_NAME = "Python for Data Analytics"
COURSE_DURATION = 12
INSTITUTE_NAME = "Vista Academy"
Another program can import these values:
import course_config
print(
course_config.COURSE_NAME
)
print(
course_config.COURSE_DURATION
)
print(
course_config.INSTITUTE_NAME
)
This is useful when certain values need to be shared across multiple parts of an application.
Python does not have a special keyword that makes a variable permanently constant.
Instead, developers normally use uppercase names to indicate that a value is intended to remain unchanged.
For example:
TAX_RATE = 0.18
MAX_STUDENTS = 50
PASSING_MARKS = 40
These names communicate intent to other developers.
For example, in an analytics module:
PASSING_MARKS = 40
def is_passed(marks):
return marks >= PASSING_MARKS
Another program can import both:
from student_tools import (
PASSING_MARKS,
is_passed
)
and use:
print(PASSING_MARKS)
print(
is_passed(75)
)
A module can contain constants that are used by its own functions.
For example:
student_tools.py
PASSING_MARKS = 40
def is_passed(marks):
return marks >= PASSING_MARKS
def get_status(marks):
if marks >= PASSING_MARKS:
return "Pass"
return "Fail"
Now the passing threshold is defined in one place.
If the business rule changes from 40 to 45, we can update:
PASSING_MARKS = 45
instead of changing the value in several functions.
This is one reason centralized constants can improve maintainability.
Let’s create a more practical analytics module.
Create:
sales_tools.py
Add:
MINIMUM_SALE = 0
def calculate_total(sales):
return sum(sales)
def calculate_average(sales):
if not sales:
return 0
return sum(sales) / len(sales)
def validate_sale(value):
return value >= MINIMUM_SALE
def highest_sale(sales):
if not sales:
return None
return max(sales)
Now use the module from another file:
import sales_tools
sales = [
10000,
15000,
12000,
18000
]
print(
"Total:",
sales_tools.calculate_total(sales)
)
print(
"Average:",
sales_tools.calculate_average(sales)
)
print(
"Highest:",
sales_tools.highest_sale(sales)
)
We can also validate individual values:
for sale in sales:
if sales_tools.validate_sale(sale):
print(
sale,
"is valid"
)
This is a simple example of how reusable business logic can be placed inside a module.
Python also allows us to give an imported function a different local name.
For example:
from analytics_tools import (
calculate_average as average
)
Now we can use:
result = average(
[10, 20, 30]
)
print(result)
This can be useful when a function name is long or when a naming conflict exists.
However, aliases should make code clearer, not more confusing.
Suppose our program already contains a function called:
calculate_average()
and we also want to import another function with the same name.
Using an alias can help:
from analytics_tools import (
calculate_average as module_average
)
Now we can distinguish between them:
local_average()
module_average()
Another simple solution is to import the complete module:
import analytics_tools
and use:
analytics_tools.calculate_average()
This keeps the source of the function explicit.
Modules can also contain classes.
For example, create:
student.py
and write:
class Student:
def __init__(self, name, marks):
self.name = name
self.marks = marks
def is_passed(self):
return self.marks >= 40
Now another program can import the class:
from student import Student
Then create an object:
student = Student(
"Rahul",
85
)
print(
student.name
)
print(
student.is_passed()
)
This shows that a module can contain both functions and classes.
In a larger application, you may have values that multiple modules need to access.
A configuration module can be useful for this purpose.
For example:
config.py
APP_NAME = "Student Analytics"
PASSING_MARKS = 40
MAX_MARKS = 100
DEFAULT_LANGUAGE = "English"
Another module can import the required values:
from config import (
PASSING_MARKS,
MAX_MARKS
)
Now the validation function can use them:
def validate_marks(marks):
return (
PASSING_MARKS
<= marks
<= MAX_MARKS
)
This structure keeps configuration values separate from application logic.
In professional applications, modules are often used to separate business logic from the main application flow.
For example, suppose a sales application needs to calculate discounts.
Instead of putting the complete discount logic inside main.py, create:
discount.py
and write:
PREMIUM_DISCOUNT = 0.20
REGULAR_DISCOUNT = 0.10
def calculate_discount(
amount,
customer_type
):
if customer_type == "premium":
return amount * PREMIUM_DISCOUNT
if customer_type == "regular":
return amount * REGULAR_DISCOUNT
return 0
The main program can now use:
from discount import calculate_discount
discount = calculate_discount(
10000,
"premium"
)
print(discount)
This makes the main program easier to read.
Good modules should be understandable to other developers.
You can use a module-level docstring:
"""
Utility functions for basic sales analysis.
"""
def calculate_total(values):
return sum(values)
You can also document individual functions:
def calculate_average(values):
"""
Calculate the average of numeric values.
"""
if not values:
return 0
return sum(values) / len(values)
Documentation becomes increasingly useful when modules are shared across multiple projects or teams.
A simple Data Analytics project could eventually look like:
analytics_project/
│
├── main.py
│
├── config.py
│
├── data_loader.py
│
├── validation.py
│
├── analytics_tools.py
│
├── report.py
│
└── visualization.py
The responsibilities might be:
| Module | Purpose |
|---|---|
config.py |
Shared configuration and constants |
data_loader.py |
Loading data |
validation.py |
Checking data validity |
analytics_tools.py |
Analytical calculations |
report.py |
Generating reports |
visualization.py |
Chart-related functionality |
main.py |
Controlling the workflow |
This is a simple example, but it demonstrates how modular Python projects can be structured logically.
When creating your own Python modules, keep these principles in mind:
if __name__ == "__main__": for direct execution or module testing.Create a module named:
student_tools.py
Add:
PASSING_MARKS = 40
def calculate_average(marks):
...
def get_grade(percentage):
...
def is_passed(marks):
...
Then create:
main.py
Import the functions and constant and test them using:
marks = [
75,
82,
68,
91,
55
]
Display:
The purpose of this exercise is to practice moving reusable logic into a separate Python module instead of writing everything in main.py.
In the next part, we will bring these concepts together by building a complete multi-module Python project and practicing module imports, reusable functions, validation, error handling, and project organization.
अब तक आपने Python modules को समझ लिया है, custom modules बनाना सीख लिया है, और functions, variables तथा constants को दूसरे Python programs में import करना भी सीख लिया है। अब इन सभी concepts को एक practical project में combine करते हैं.
हम एक simple Student Performance Analytics System बनाएंगे। इस project का उद्देश्य केवल output प्राप्त करना नहीं है, बल्कि यह समझना है कि Python में एक छोटे application को अलग-अलग reusable modules में कैसे organize किया जाता है.
हम project को कई files में divide करेंगे:
student_analytics/
│
├── main.py
├── student_data.py
├── validation.py
├── analysis_tools.py
└── report.py
हर file की एक specific responsibility होगी.
| Module | Purpose |
|---|---|
student_data.py |
Student data store करना |
validation.py |
Student records validate करना |
analysis_tools.py |
Calculations perform करना |
report.py |
Results display करना |
main.py |
पूरे workflow को control करना |
यह structure हमें modular programming का practical experience देगा.
सबसे पहले student_data.py बनाइए.
students = [
{
"id": 101,
"name": "Rahul",
"marks": 85
},
{
"id": 102,
"name": "Priya",
"marks": 92
},
{
"id": 103,
"name": "Amit",
"marks": 76
},
{
"id": 104,
"name": "Neha",
"marks": 88
},
{
"id": 105,
"name": "Ravi",
"marks": 69
}
]
यह module फिलहाल केवल student records रख रहा है.
अब main.py में इसे import किया जा सकता है:
from student_data import students
print(students)
इस approach का फायदा यह है कि main program के अंदर data hard-code करने की जरूरत नहीं है.
अब दूसरी file बनाइए:
validation.py
इस module का काम केवल यह check करना होगा कि student record सही structure और valid marks रखता है या नहीं.
def validate_student(student):
required_fields = [
"id",
"name",
"marks"
]
for field in required_fields:
if field not in student:
return False
marks = student["marks"]
if not isinstance(
marks,
(int, float)
):
return False
if marks < 0 or marks > 100:
return False
return True
अब validation logic अलग module में है.
इसका अर्थ है कि यदि भविष्य में validation rules बदलते हैं, तो हमें मुख्य program में बदलाव करने की आवश्यकता नहीं होगी. हम केवल validation.py में बदलाव कर सकते हैं.
अब main.py में function import करें:
from validation import validate_student
हम सभी student records को check कर सकते हैं:
valid_students = []
for student in students:
if validate_student(student):
valid_students.append(student)
अब valid_students में केवल valid records होंगे.
इस प्रकार हमारा workflow बन गया:
Student Data
↓
Validation
↓
Valid Student Records
अब तीसरी important functionality बनाते हैं.
File:
analysis_tools.py
इसमें student marks से related functions लिखें:
def calculate_total(students):
return sum(
student["marks"]
for student in students
)
def calculate_average(students):
if not students:
return 0
total = calculate_total(students)
return total / len(students)
def find_highest(students):
if not students:
return None
return max(
students,
key=lambda student: student["marks"]
)
def find_lowest(students):
if not students:
return None
return min(
students,
key=lambda student: student["marks"]
)
अब सभी analytical calculations एक reusable module में मौजूद हैं.
अब main.py में functions import करें:
from analysis_tools import (
calculate_total,
calculate_average,
find_highest,
find_lowest
)
अब हम valid students के लिए calculations कर सकते हैं:
total = calculate_total(
valid_students
)
average = calculate_average(
valid_students
)
highest = find_highest(
valid_students
)
lowest = find_lowest(
valid_students
)
अब main program में calculations की implementation दिखाई नहीं दे रही है. Main program केवल functions का उपयोग कर रहा है.
यही modular programming का बड़ा फायदा है.
अब results को display करने के लिए एक अलग module बनाएंगे.
File:
report.py
इसमें:
def display_report(
total,
average,
highest,
lowest
):
print(
"\n--- Student Performance Report ---"
)
print(
"Total Marks:",
total
)
print(
"Average Marks:",
round(average, 2)
)
print(
"Highest Student:",
highest["name"]
)
print(
"Highest Marks:",
highest["marks"]
)
print(
"Lowest Student:",
lowest["name"]
)
print(
"Lowest Marks:",
lowest["marks"]
)
अब reporting logic भी अलग module में चला गया.
main.py में report function import करें:
from report import display_report
फिर:
display_report(
total,
average,
highest,
lowest
)
अब पूरा architecture इस प्रकार है:
student_data.py
↓
Student Records
↓
validation.py
↓
Valid Records
↓
analysis_tools.py
↓
Statistics
↓
report.py
↓
Final Report
अब सभी modules को connect करके main.py तैयार करते हैं.
from student_data import students
from validation import validate_student
from analysis_tools import (
calculate_total,
calculate_average,
find_highest,
find_lowest
)
from report import display_report
def run_analysis():
valid_students = []
for student in students:
if validate_student(student):
valid_students.append(student)
if not valid_students:
print(
"No valid student records found."
)
return
total = calculate_total(
valid_students
)
average = calculate_average(
valid_students
)
highest = find_highest(
valid_students
)
lowest = find_lowest(
valid_students
)
display_report(
total,
average,
highest,
lowest
)
if __name__ == "__main__":
run_analysis()
अब हमारा main program केवल workflow manage करता है.
इसमें data storage, validation logic, analytical formulas और reporting implementation अलग-अलग modules में हैं.
main.py का उद्देश्य हर calculation को खुद करना नहीं है.
इसका काम modules को सही sequence में use करना है.
हमारा application flow है:
Load Data
↓
Validate
↓
Analyze
↓
Generate Report
यह pattern Data Analytics projects में बहुत useful है.
For example, future में इसी structure को बड़े workflow में बदला जा सकता है:
Load CSV
↓
Validate Data
↓
Clean Data
↓
Calculate KPIs
↓
Create Visualization
↓
Generate Report
हर stage को एक अलग module में रखा जा सकता है.
अब project को थोड़ा और realistic बनाते हैं. अगर कोई student record invalid है, तो हम custom exception भी use कर सकते हैं.
validation.py में:
class StudentDataError(Exception):
pass
class InvalidMarksError(StudentDataError):
pass
अब validation function को update कर सकते हैं:
def validate_marks(marks):
if not isinstance(
marks,
(int, float)
):
raise InvalidMarksError(
"Marks must be numeric."
)
if marks < 0 or marks > 100:
raise InvalidMarksError(
"Marks must be between 0 and 100."
)
return True
यह function केवल marks validation के लिए जिम्मेदार है.
अब main.py में exception handle किया जा सकता है:
from validation import (
validate_marks,
InvalidMarksError
)
try:
validate_marks(150)
except InvalidMarksError as error:
print(
"Validation Error:",
error
)
यह example दिखाता है कि अलग modules में exception classes और functions बनाए जा सकते हैं और फिर main application में उन्हें import करके handle किया जा सकता है.
यह concept आपने Exception Handling chapter में सीखा था. अब आप उसे modules के साथ combine कर रहे हैं.
बड़े projects में कुछ common values कई modules को चाहिए होती हैं.
इसके लिए हम एक configuration module बना सकते हैं:
config.py
इसमें:
PASSING_MARKS = 40
MAX_MARKS = 100
COURSE_NAME = "Python for Data Analytics"
अब validation module इसे import कर सकता है:
from config import (
PASSING_MARKS,
MAX_MARKS
)
और validation logic में use कर सकता है:
def validate_marks(marks):
if marks < PASSING_MARKS:
return False
if marks > MAX_MARKS:
return False
return True
इस approach से important configuration values एक central location पर रखी जा सकती हैं.
अब हमारा project थोड़ा अधिक organized दिखाई देगा:
student_analytics/
│
├── main.py
├── config.py
├── student_data.py
├── validation.py
├── analysis_tools.py
└── report.py
यह structure अभी छोटा है, लेकिन इसमें professional modular programming के important concepts दिखाई देते हैं.
मान लीजिए सभी code एक ही file में रखा गया:
main.py
Data
Validation
Calculations
Exceptions
Reports
Configuration
Everything together
जैसे-जैसे project बढ़ेगा, file को समझना कठिन हो सकता है.
Modular approach में:
Data → student_data.py
Validation → validation.py
Analysis → analysis_tools.py
Reports → report.py
Config → config.py
Workflow → main.py
हर module का purpose स्पष्ट है.
अब इसी concept को sales dataset पर लागू करें.
एक folder बनाएं:
sales_project/
और files बनाएं:
sales_data.py
sales_analysis.py
sales_report.py
main.py
sales_data.py में:
sales = [
10000,
15000,
22000,
18000,
25000
]
sales_analysis.py में functions बनाएं:
calculate_total()
calculate_average()
find_highest()
find_lowest()
sales_report.py में report function बनाएं.
फिर main.py में सभी modules import करके complete sales analysis चलाएं.
इस exercise का उद्देश्य है कि आप केवल syntax याद न करें, बल्कि modules के बीच relationship समझें.
Wrong module name:
import analytics_tool
जब actual file है:
analytics_tools.py
इससे import error आ सकता है.
Wrong function name:
from analytics_tools import average_sales
जब module में function का नाम है:
calculate_average()
Python में names exact होने चाहिए.
Wrong folder structure:
अगर custom module Python को expected location पर नहीं मिलता, तो ModuleNotFoundError आ सकता है.
Unnecessary duplicate code:
अगर एक ही function कई modules में copy किया जा रहा है, तो देखें कि उसे reusable utility module में रखा जा सकता है या नहीं.
What is a custom Python module?
A custom Python module is a Python file created by the programmer to organize and reuse functions, variables, classes, or other code.
Why are modules useful?
They improve code organization, reusability, readability, and maintainability.
Can one Python module import another module?
Yes. Python modules can import functionality from other modules.
What is the purpose of main.py?
It commonly acts as the entry point of an application and coordinates functionality provided by other modules.
Can a module contain constants?
Yes. Constants can be stored in a module and imported wherever required.
Why use if __name__ == "__main__":?
It allows code to run when the file is executed directly without automatically running that code when the module is imported.
Can custom exceptions be stored in a module?
Yes. Exception classes can be defined in one module and imported into another module.
इस lesson का सबसे important concept है separation of responsibilities.
student_data.py
↓
Data
validation.py
↓
Validation
analysis_tools.py
↓
Analysis
report.py
↓
Reporting
main.py
↓
Workflow
और modules को जोड़ने के लिए:
import module
from module import function
का उपयोग किया जाता है.
जब project बड़ा होता है, तो modular structure code को अधिक manageable बनाता है. Data Analytics में यही approach आगे चलकर data loading, cleaning, analysis, visualization और reporting जैसे अलग-अलग components को organize करने में मदद करती है.
इस lesson के बाद आपको सक्षम होना चाहिए:
main.py को application entry point की तरह use करना.if __name__ == "__main__": का सही उपयोग करना.Python Modules chapter का यह lesson complete है.