Creating and Importing Custom Modules in Python
Course: Python Training
Topic: Creating and Importing Custom Modules
Course Link: Python Training Course – JustAcademy
Demo Link: Register for a Course Demo
Website: JustAcademy
1. Introduction to Python Modules
A module in Python is a file containing Python code, such as variables, functions, classes, and executable statements. Modules help organize programs into smaller, reusable components instead of placing all the code in a single file.
Python includes many built-in and standard-library modules, such as math, random, datetime, and os. In addition to these, developers can create their own modules to organize application-specific functionality.
For example, an application might have separate modules for calculating prices, validating user input, managing student records, and generating reports. Each module can focus on a specific responsibility and be imported wherever its functionality is required.
Why Are Custom Modules Important?
- Code reuse: Write a function once and use it in multiple Python files.
- Better organization: Separate related functionality into logical files.
- Maintainability: Update one module without unnecessarily changing unrelated code.
- Readability: Keep large applications easier to understand.
- Testing: Test individual functions and components independently.
- Collaboration: Allow different developers to work on separate modules.
- Scalability: Build larger applications using smaller, manageable components.
2. Understanding the Structure of a Python Module
Every Python file with a .py extension can generally be imported as a module when it is accessible through Python's import system. A module can contain functions, classes, constants, and other statements.
Example: Creating a Custom Module
Create a file named calculator.py:
# calculator.py
PI = 3.14159
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiply(a, b):
return a * b
def divide(a, b):
if b == 0:
raise ValueError("The divisor cannot be zero.")
return a / b
This module contains a constant named PI and four functions for basic arithmetic operations. Other Python files can import these functions instead of rewriting the same calculations.
Understanding the Components
| Component |
Example |
Purpose |
| Constant |
PI = 3.14159 |
Stores a reusable value. |
| Function |
add(a, b) |
Performs a specific operation. |
| Conditional logic |
if b == 0: |
Handles a special condition. |
| Exception |
raise ValueError(...) |
Reports invalid input explicitly. |
3. Creating Your First Custom Module
To create a custom module, save reusable Python code in a separate .py file. Place that file where Python can find it when another script imports it.
Step 1: Create a Module File
Create a file named greetings.py containing the following code:
# greetings.py
def say_hello(name):
return f"Hello, {name}!"
def say_goodbye(name):
return f"Goodbye, {name}!"
def welcome_message():
return "Welcome to Python programming."
Step 2: Create a Main Program
Create another file named main.py in the same directory:
# main.py
import greetings
print(greetings.say_hello("Rahul"))
print(greetings.say_goodbye("Rahul"))
print(greetings.welcome_message())
Output
Hello, Rahul!
Goodbye, Rahul!
Welcome to Python programming.
Step 3: Understand the Execution
- Python starts executing
main.py.
- The statement
import greetings loads the module if it has not already been loaded in the current interpreter session.
- The module name becomes available in the main program's namespace.
- The functions are accessed using the module name followed by a dot.
- The function calls return strings that are printed to the screen.
This structure allows the greeting functions to be reused in any other script that can import greetings.
4. Importing a Complete Module Using import
The simplest way to import a custom module is to use the import statement followed by the module name without the .py extension.
Syntax
import module_name
Example
Suppose calculator.py contains the arithmetic functions introduced earlier. In main.py, write:
import calculator
result1 = calculator.add(10, 5)
result2 = calculator.multiply(4, 3)
print(result1)
print(result2)
Output:
15
12
When a complete module is imported, its members are normally accessed through the module namespace. This makes it clear where each function or constant comes from and reduces the risk of name conflicts.
Accessing Module Variables
import calculator
print(calculator.PI)
print(calculator.add(2, 3))
Using calculator.PI makes the source of the constant explicit. This can be especially helpful when a program uses several modules that define similarly named variables.
5. Importing Specific Functions and Variables
Python allows you to import selected functions, classes, or variables directly from a module using the from ... import ... syntax.
Syntax
from module_name import member_name
Example
from calculator import add, multiply
print(add(10, 20))
print(multiply(5, 4))
Output:
30
20
Because the selected functions are imported directly, you can call add() and multiply() without writing calculator. before each name.
Importing a Constant and a Function
from calculator import PI, divide
print("PI:", PI)
print("Result:", divide(20, 4))
Advantages of Selective Imports
- Makes it clear which module members the file needs.
- Can reduce repetitive module-name prefixes.
- Works well when only a few functions are required.
Potential Disadvantages
- The source module may be less obvious when reading the function call.
- Imported names can conflict with names defined elsewhere.
- Large selective-import statements may become difficult to maintain.
6. Using Aliases with the as Keyword
The as keyword assigns an alternative name to an imported module or member. Aliases are useful when module names are long, when two modules have similar names, or when a shorter name improves readability.
Aliasing a Module
import calculator as calc
print(calc.add(15, 25))
print(calc.subtract(50, 10))
Output:
40
40
Aliasing an Imported Function
from calculator import multiply as product
print(product(6, 7))
Output:
42
When Should You Use Aliases?
- When a module has a long name and a clear short alias improves readability.
- When two imported modules use the same name.
- When a function name conflicts with an existing local name.
- When following established naming conventions for common libraries.
Prefer meaningful aliases instead of arbitrary abbreviations. Consistent naming makes the code easier for other developers to understand.
7. Understanding from module import *
Python supports importing names from a module using an asterisk:
from calculator import *
This form imports names that the module makes available for wildcard import. If the module defines __all__, that list controls which names are imported by this syntax. Otherwise, names that do not begin with an underscore are generally imported.
Why Is Wildcard Import Usually Discouraged?
- It can make the origin of a function or variable difficult to identify.
- It may overwrite names that already exist in the current namespace.
- It makes code harder to review and maintain.
- It can create unexpected conflicts when modules change over time.
Recommended Alternative
from calculator import add, subtract
print(add(8, 2))
print(subtract(8, 2))
Explicit imports are generally easier to understand and maintain than wildcard imports.
8. Understanding Module Search Paths
When Python imports a module, it searches for that module using its import system. The search locations commonly include the directory containing the script being run, locations supplied through the module search path, standard-library locations, and installed package directories.
The exact search behavior depends on how Python is started, the execution environment, the import mechanism, and the module configuration.
Inspecting the Module Search Path
import sys
for path in sys.path:
print(path)
The sys.path list shows directories that Python searches for modules in the relevant import context.
Example Directory Structure
python_project/
main.py
calculator.py
greetings.py
If main.py is executed from this project setup, the sibling modules will generally be importable because their directory is available to the import system.
Importing from a Subdirectory
python_project/
main.py
utilities/
text_tools.py
For a small project, a package structure with an __init__.py file can make imports more explicit and organized:
python_project/
main.py
utilities/
__init__.py
text_tools.py
You can then import a function using:
from utilities.text_tools import clean_text
print(clean_text(" Hello Python "))
This example assumes that text_tools.py defines a function named clean_text() and that the project root is available to Python's import system.
9. Organizing a Project with Multiple Custom Modules
As a Python application grows, it is helpful to divide its functionality into modules based on responsibility. This makes each file easier to understand, test, and maintain.
Example Project Structure
student_app/
main.py
student.py
grading.py
reports.py
Each module has a distinct purpose:
| File |
Responsibility |
Example Function |
student.py |
Student information and validation |
create_student() |
grading.py |
Calculate grades and results |
calculate_grade() |
reports.py |
Format and display reports |
generate_report() |
main.py |
Coordinate application execution |
main() |
student.py
def create_student(name, course):
return {
"name": name,
"course": course
}
grading.py
def calculate_grade(marks):
if not 0 <= marks <= 100:
raise ValueError("Marks must be between 0 and 100.")
if marks >= 90:
return "A"
elif marks >= 75:
return "B"
elif marks >= 60:
return "C"
elif marks >= 40:
return "D"
return "F"
reports.py
def generate_report(student, grade):
return (
f"Student: {student['name']}\n"
f"Course: {student['course']}\n"
f"Grade: {grade}"
)
main.py
from student import create_student
from grading import calculate_grade
from reports import generate_report
def main():
student = create_student("Rahul", "Python")
grade = calculate_grade(86)
report = generate_report(student, grade)
print(report)
if __name__ == "__main__":
main()
Output
Student: Rahul
Course: Python
Grade: B
This project demonstrates separation of concerns: each module performs a focused task, and the main file coordinates their use.
10. Understanding the __name__ Variable
Every Python module has a special attribute named __name__. When a file is executed as the main program, its __name__ is set to "__main__". When it is imported under its module name, __name__ normally contains that module's name.
Example
# greetings.py
def say_hello(name):
return f"Hello, {name}!"
if __name__ == "__main__":
print(say_hello("Developer"))
print("This file is being run directly.")
If you run greetings.py directly, the guarded block executes. If another file imports greetings, the function becomes available, but the guarded block does not run merely because of the import.
Why Use the __name__ == "__main__" Check?
- Prevents demonstration code from running whenever the module is imported.
- Allows a module to be both reusable and directly executable.
- Provides a clear entry point for small scripts and applications.
- Makes it easier to write simple manual tests for a module.
Recommended Main Function Pattern
def main():
print("Application started.")
if __name__ == "__main__":
main()
Using a main() function keeps the application's entry point organized and helps separate execution logic from reusable definitions.
11. Understanding What Happens During Import
When Python imports a module for the first time in a process, it locates the module, creates a module object, and executes the module's top-level code. The module is then normally stored in sys.modules, allowing later imports to reuse the loaded module rather than executing it again in the usual case.
Example
# sample_module.py
print("sample_module is being initialized")
VALUE = 100
def show_value():
print(VALUE)
Now create main.py:
import sample_module
import sample_module
sample_module.show_value()
In a typical execution of main.py, the initialization message is printed once because the second import reuses the already loaded module.
Important Concepts
- Top-level execution: Statements outside functions and classes execute when the module is first imported.
- Module caching: Python normally stores imported modules in
sys.modules.
- Import side effects: Top-level file operations, print statements, or other actions can occur during import.
- Import order: Dependencies and initialization order can affect program behavior.
For reusable modules, keep top-level code focused on definitions and essential initialization. Avoid performing expensive or unexpected operations merely because another module imports the file.
12. Understanding Relative and Absolute Imports
Python packages allow related modules to be organized into directories. Imports inside packages can use absolute or relative import syntax.
Example Package Structure
shop_app/
main.py
shop/
__init__.py
products.py
pricing.py
Absolute Import
An absolute import specifies the full package path from a location available to the import system.
from shop.products import get_product
from shop.pricing import calculate_discount
Relative Import
Inside a package module, a relative import uses leading dots to refer to the current package or its parent packages.
# Inside shop/pricing.py
from .products import get_product
The single leading dot refers to the current package. Two leading dots refer to the parent package, where the package structure permits it.
Important Difference
| Import Type |
Example |
Use |
| Absolute |
from shop.products import get_product |
Clearly identifies a module through its package path. |
| Relative |
from .products import get_product |
References another module within the same package. |
Relative imports depend on package context and are generally used within packages. Running a package module directly as a standalone file can cause relative-import errors. Use an appropriate package execution method, such as python -m package.module, from the correct project location.
13. Creating a Custom Utility Module
Utility modules contain reusable functions that support multiple parts of an application. Common examples include string formatting, validation, date conversion, mathematical calculations, and file-processing helpers.
Example: text_utils.py
# text_utils.py
def clean_text(text):
return text.strip()
def count_words(text):
return len(text.split())
def make_title(text):
return text.strip().title()
Using the Utility Module
# main.py
import text_utils
message = " learning python modules "
print(text_utils.clean_text(message))
print(text_utils.count_words(message))
print(text_utils.make_title(message))
Output
learning python modules
3
Learning Python Modules
The same utility module can be imported into other scripts, avoiding duplicated string-processing logic.
Design Guidelines for Utility Modules
- Give the module a descriptive name that reflects its responsibility.
- Keep related functions together.
- Use clear function names and docstrings.
- Validate inputs when invalid values could cause unexpected behavior.
- Avoid unrelated functionality in the same module.
- Write tests for functions that are reused across the application.
14. Creating a Module with Classes
A custom module can contain classes as well as functions. This is useful when a program needs reusable object-oriented components.
Example: employee.py
# employee.py
class Employee:
def __init__(self, name, department, salary):
self.name = name
self.department = department
self.salary = salary
def display_details(self):
return (
f"Name: {self.name}, "
f"Department: {self.department}, "
f"Salary: {self.salary}"
)
Importing the Class
# main.py
from employee import Employee
employee = Employee("Neha", "IT", 65000)
print(employee.display_details())
Output
Name: Neha, Department: IT, Salary: 65000
The class is defined in a separate module and imported into the main program. This approach supports reuse across multiple applications and encourages separation of responsibilities.
15. Using __all__ to Control Wildcard Imports
A module can define the special variable __all__ to specify which names are made available by a wildcard import such as from module_name import *.
Example: geometry.py
# geometry.py
__all__ = ["area_rectangle", "area_square"]
def area_rectangle(length, width):
return length * width
def area_square(side):
return side * side
def _internal_helper():
return "Internal implementation detail"
Importing the Module Members
from geometry import *
print(area_rectangle(10, 5))
print(area_square(4))
The wildcard import exposes the names listed in __all__. However, explicitly importing required functions is still generally preferable for readability and maintainability.
Defining __all__ does not prevent other code from explicitly importing a name that is not listed. It controls wildcard-import behavior rather than enforcing access restrictions.
16. Avoiding Module Naming Conflicts
Python can import your custom module instead of an intended standard-library or third-party module if both names conflict and the custom file appears earlier in the relevant search locations.
Example of a Problematic Filename
random.py
If you create a custom file named random.py, a statement such as import random may load your file instead of Python's standard-library random module. This can cause confusing import errors or unexpected behavior.
Recommended Practices
- Avoid naming custom files after standard-library modules, such as
random.py, csv.py, math.py, or json.py.
- Avoid naming files after important third-party packages used by your project.
- Choose descriptive, project-specific module names.
- If a naming conflict occurs, rename the custom file and remove stale cache files where appropriate.
- Restart the Python process if the conflicting module has already been loaded and cached.
17. Understanding Circular Imports
A circular import occurs when two or more modules depend on each other in a cycle. For example, module_a.py imports module_b.py, while module_b.py imports module_a.py.
Example of a Circular Dependency
# module_a.py
from module_b import function_b
def function_a():
return "Function A"
# module_b.py
from module_a import function_a
def function_b():
return "Function B"
This arrangement can fail because one module may try to access a name before the other module has finished initializing. The exact behavior depends on the import order and when the names are accessed.
Ways to Reduce Circular Imports
- Reconsider responsibilities: Move shared functionality into a third module.
- Reduce coupling: Avoid making two modules depend heavily on each other.
- Use clearer interfaces: Have one module provide functions that another module calls, instead of sharing internal details.
- Use local imports selectively: Import inside a function when the dependency is genuinely needed only there. This can help in some cases, but it should not replace sound architecture.
Improved Project Structure
project/
shared_utils.py
module_a.py
module_b.py
When both modules need the same helper, place it in shared_utils.py so the dependency can be directed toward a common module rather than forming a cycle.
18. Reloading a Custom Module During Development
Python normally caches imported modules. If you edit a module after importing it, importing it again does not necessarily execute the updated code in the same way as a fresh interpreter session.
The importlib.reload() function can re-execute a previously imported module during development.
Example
import calculator
import importlib
print(calculator.add(2, 3))
# After editing calculator.py, reload the module.
importlib.reload(calculator)
print(calculator.add(2, 3))
Reloading is useful for experimentation, but it has limitations. Existing references to objects imported directly with from calculator import add may still refer to the previously imported function. Reloading a module also does not automatically update every object or reference created earlier.
Recommended Development Approach
- Restart the interpreter when testing major module changes.
- Use reload only when you understand the module-caching behavior.
- Prefer automated tests to verify that module changes work as intended.
- Avoid relying on reload behavior in ordinary production application logic.
19. Custom Modules Versus Packages
A module is generally a single Python file, whereas a package organizes multiple related modules under a common namespace.
| Feature |
Module |
Package |
| Basic structure |
Usually one .py file |
Usually a directory containing related modules |
| Example |
calculator.py |
utilities/ |
| Organization |
Suitable for a focused set of functions or classes |
Suitable for a group of related modules |
| Import example |
import calculator |
from utilities import text_tools |
| Typical use |
Small reusable components |
Larger, structured codebases |
Example Package Structure
utilities/
__init__.py
calculations.py
text_tools.py
validation.py
The __init__.py file can be used to initialize a regular package and expose selected package-level names. Python also supports namespace packages that do not require an __init__.py file in every package directory.
20. Common Errors When Creating and Importing Modules
| Error or Problem |
Possible Cause |
Solution |
ModuleNotFoundError |
Python cannot locate the requested module. |
Check the filename, directory, environment, and import path. |
ImportError |
A requested name cannot be imported or a dependency is not available as expected. |
Verify the exported name and module initialization. |
AttributeError |
The module does not contain the referenced attribute. |
Check the function name and the imported module. |
| Unexpected output during import |
The module executes top-level statements. |
Move demonstration or application execution into a guarded main function. |
| Circular import problem |
Modules depend on one another in a cycle. |
Refactor shared code into a separate module. |
| Wrong module imported |
A local file shadows another module. |
Rename the conflicting file and inspect the import path. |
| Changes do not appear |
The module is cached or old references remain in use. |
Restart the interpreter or reload appropriately during development. |
| Relative import fails |
A package module is executed outside its package context. |
Run it using an appropriate package execution method. |
Example: Diagnosing ModuleNotFoundError
import sys
print("Python executable:", sys.executable)
print("Import search paths:")
for path in sys.path:
print(path)
This diagnostic code helps identify which Python interpreter is running and which paths are available for module discovery.
Example: Diagnosing the Imported Module
import calculator
print(calculator.__file__)
The __file__ attribute often shows the file from which a module was loaded. Some modules, including certain built-in or frozen modules, may not have a conventional file path.
21. Best Practices for Creating Custom Modules
- Use descriptive names: Choose filenames that communicate the module's purpose.
- Follow naming conventions: Use lowercase names and underscores when appropriate, such as
data_validator.py.
- Follow the single-responsibility principle: Keep related functionality together and avoid mixing unrelated tasks.
- Use explicit imports: Prefer
import module or selective imports over wildcard imports.
- Keep import-time behavior minimal: Avoid unnecessary file operations, user prompts, or long-running work at the module's top level.
- Use a main guard: Place script-specific execution inside
if __name__ == "__main__":.
- Document reusable functions: Add docstrings describing purpose, parameters, return values, and exceptions.
- Avoid circular dependencies: Organize modules so that dependencies remain clear.
- Validate inputs: Check arguments and handle invalid values appropriately.
- Test independently: Write unit tests for important module functions.
- Use packages for larger projects: Group related modules under a clear package structure.
- Avoid naming conflicts: Do not shadow standard-library or important third-party module names.
- Manage dependencies: Keep external dependencies explicit and use an appropriate virtual environment.
22. Practical Project: Reusable Expense Calculator
Build a small expense-tracking program using multiple custom modules. The project demonstrates how to separate calculations, validation, and application execution.
Project Structure
expense_tracker/
main.py
calculations.py
validation.py
reports.py
File 1: calculations.py
def calculate_total(expenses):
return sum(expenses)
def calculate_average(expenses):
if not expenses:
return 0
return calculate_total(expenses) / len(expenses)
def calculate_remaining_budget(budget, expenses):
return budget - calculate_total(expenses)
File 2: validation.py
def validate_amount(amount):
if amount < 0:
raise ValueError("Expense amount cannot be negative.")
return amount
def validate_budget(budget):
if budget < 0:
raise ValueError("Budget cannot be negative.")
return budget
File 3: reports.py
def create_report(budget, expenses, total, average, remaining):
lines = [
"EXPENSE REPORT",
f"Initial budget: {budget:.2f}",
f"Number of expenses: {len(expenses)}",
f"Total expenses: {total:.2f}",
f"Average expense: {average:.2f}",
f"Remaining budget: {remaining:.2f}"
]
return "\n".join(lines)
File 4: main.py
from calculations import (
calculate_total,
calculate_average,
calculate_remaining_budget
)
from validation import validate_amount, validate_budget
from reports import create_report
def main():
budget = validate_budget(10000)
expenses = [
validate_amount(1200),
validate_amount(850),
validate_amount(600),
validate_amount(450)
]
total = calculate_total(expenses)
average = calculate_average(expenses)
remaining = calculate_remaining_budget(budget, expenses)
report = create_report(
budget,
expenses,
total,
average,
remaining
)
print(report)
if __name__ == "__main__":
main()
Expected Output
EXPENSE REPORT
Initial budget: 10000.00
Number of expenses: 4
Total expenses: 3100.00
Average expense: 775.00
Remaining budget: 6900.00
Project Learning Outcomes
- Creating multiple custom modules.
- Importing functions from separate files.
- Separating validation, calculation, and report generation.
- Handling invalid values using exceptions.
- Using a main function as the application's entry point.
- Building reusable code that can be tested independently.
23. Practice Exercises
Beginner Exercises
- Create a module named
greetings.py containing functions for welcoming and saying goodbye to users.
- Create a
calculator.py module with addition, subtraction, multiplication, and division functions.
- Import a constant and two functions from a custom module using
from ... import ....
- Import a module using an alias and call its functions.
- Add a main guard to a module containing reusable functions and demonstration code.
Intermediate Exercises
- Create a
string_utils.py module that counts words, removes surrounding whitespace, and formats titles.
- Create separate modules for student records, grading, and report generation.
- Use
pathlib and a package structure to organize utility modules.
- Define
__all__ and test how wildcard imports behave.
- Inspect
sys.path to understand why Python cannot locate a custom module.
Advanced Exercises
- Build a modular inventory application with product, validation, and reporting modules.
- Refactor a large Python script into smaller modules with clear responsibilities.
- Identify and resolve a circular import by moving shared functionality into a third module.
- Write unit tests for a custom module using Python's
unittest framework.
- Create a package containing several modules and run its main application using an appropriate package execution method.
24. Interview Questions and Answers
Q1. What is a module in Python?
Answer: A module is a Python file containing reusable code such as functions, classes, variables, and executable statements.
Q2. How do you create a custom module?
Answer: Create a Python file with a .py extension and define the required functions, classes, or variables inside it.
Q3. How do you import a custom module?
Answer: Use the import module_name statement, provided the module is accessible through Python's import system.
Q4. What is the difference between import and from import?
Answer: import module makes the module available under its module name. from module import member imports selected members directly into the current namespace.
Q5. What is an import alias?
Answer: An alias is an alternative name assigned to an imported module or member using the as keyword.
Q6. What is the purpose of __name__?
Answer: The __name__ attribute identifies the module's execution name. When a file is executed as the main program, its value is "__main__".
Q7. Why is the main guard used?
Answer: It prevents code inside the guarded block from running merely because the module is imported, while still allowing that code to run when the file is executed directly.
Q8. What is the difference between a module and a package?
Answer: A module is generally a single Python file, while a package organizes related modules under a shared namespace.
Q9. What is a circular import?
Answer: A circular import occurs when modules depend on one another in a cycle, which can lead to partially initialized modules or unavailable names during import.
Q10. What is sys.path?
Answer: sys.path is a list of locations used by Python's import system when searching for modules in the relevant execution context.
Q11. What is the purpose of __all__?
Answer: __all__ specifies the names exposed by a module's wildcard import behavior when using from module import *.
Q12. Why should wildcard imports generally be avoided?
Answer: They can introduce unexpected names, cause conflicts, and make it difficult to identify where a function or variable originated.
Q13. Why should custom modules avoid top-level side effects?
Answer: Top-level statements execute during initial import. Unexpected file operations, prompts, or expensive tasks can therefore run when another file simply imports the module.
Q14. How can you troubleshoot ModuleNotFoundError?
Answer: Check the module filename, project directory, Python interpreter, virtual environment, and relevant search paths. Also ensure that a conflicting local file is not affecting imports.
Q15. What does importlib.reload() do?
Answer: It re-executes a previously imported module. However, existing references to previously imported objects may not automatically update, so reloading requires care.
25. Quick Revision Checklist
- Understand the meaning and benefits of custom modules.
- Create reusable Python files containing functions, variables, and classes.
- Import complete modules using
import.
- Import selected members using
from ... import ....
- Use aliases with the
as keyword when appropriate.
- Understand why wildcard imports are usually discouraged.
- Inspect
sys.path when troubleshooting module discovery.
- Use
if __name__ == "__main__": to protect script-specific execution.
- Understand module initialization and caching.
- Organize related modules into packages.
- Know the difference between absolute and relative imports.
- Recognize circular imports and refactor dependencies when needed.
- Avoid filenames that shadow standard-library or third-party modules.
- Keep modules focused, documented, reusable, and testable.
26. Conclusion
Creating and importing custom modules is an essential Python skill that supports reusable, organized, and maintainable software. By placing related functions, classes, and constants in separate files, developers can divide complex applications into smaller components that are easier to understand and test.
Start by creating simple modules and importing them with import or from ... import .... As projects grow, learn to organize modules into packages, use the main guard, understand Python's import search path, and avoid circular dependencies and naming conflicts.
Practice these concepts by building a modular calculator, student management system, expense tracker, or inventory application. These projects will help you apply custom modules in realistic Python development workflows.
Continue learning: Explore the Python Training Course at JustAcademy, register for a course demo, or visit the JustAcademy website.