As Python projects become larger, a single collection of modules may not be enough to keep the project organized. A project may contain separate modules for data loading, validation, analysis, visualization, reporting, configuration, and other tasks. When the number of modules increases, Python provides another level of organization called a package.
A Python package allows related Python modules to be grouped together inside a directory. In simple terms, you can think of a module as one Python file and a package as a folder that organizes related modules.
For example, imagine a Data Analytics project containing these modules:
data_loader.py
data_cleaning.py
statistics.py
visualization.py
reporting.py
If all these files are placed in one large project directory, the project may become difficult to navigate as more functionality is added.
Instead, we can organize them into packages:
analytics/
│
├── data_loader.py
├── data_cleaning.py
├── statistics.py
├── visualization.py
└── reporting.py
Here, analytics represents a logical group containing related Python modules.
This organization becomes especially useful when working on larger Python applications.
The difference between a module and a package is important.
A module is generally a single Python file:
analytics.py
A package is a directory used to organize related Python modules:
analytics/
│
├── data.py
├── statistics.py
└── visualization.py
A useful mental model is:
Package
↓
Collection of related modules
↓
Each module contains reusable Python code
This allows Python projects to be divided into logical sections.
Packages provide several important advantages.
For example, consider a complete Data Analytics application.
Instead of:
project/
│
├── data_loader.py
├── data_cleaning.py
├── statistics.py
├── visualization.py
├── dashboard.py
├── report.py
├── database.py
├── api.py
└── configuration.py
we could organize the application like this:
project/
│
├── data/
│ ├── loader.py
│ └── cleaning.py
│
├── analytics/
│ └── statistics.py
│
├── visualization/
│ └── charts.py
│
├── reporting/
│ └── report.py
│
└── main.py
This structure makes the responsibilities of different parts of the project easier to understand.
Let’s create a basic package from scratch.
Create a project folder:
my_project/
Inside it, create:
tools/
Inside the tools folder, create a module:
calculator.py
So the basic structure becomes:
my_project/
│
├── main.py
│
└── tools/
└── calculator.py
Inside calculator.py:
def add(a, b):
return a + b
def subtract(a, b):
return a - b
Now we have a module inside a package-like directory structure.
The next step is learning how to import the module from the package.
From main.py, we can use package-based import syntax:
from tools import calculator
Then:
result = calculator.add(
10,
5
)
print(result)
Output:
15
Here:
tools
is the package name, while:
calculator
is the module inside the package.
The general pattern is:
from package import module
This becomes very useful when a project contains multiple related modules.
You can also import a specific function.
from tools.calculator import add
Now you can use:
print(
add(20, 10)
)
The structure:
tools.calculator
means:
tools
↓
package
calculator
↓
module
and:
add
↓
function inside the module
Packages make dot notation particularly useful for organizing code.
For example:
tools.calculator.add()
can be understood as:
tools
↓
calculator
↓
add()
This creates a clear hierarchy.
In a large application, you might see something like:
analytics.statistics.mean()
which conceptually represents:
analytics
↓
statistics
↓
mean()
This makes the location and purpose of functionality easier to understand.
Packages can be particularly useful in Data Analytics projects because analytics applications often contain several different areas of functionality.
For example:
analytics_project/
│
├── data/
│ ├── loader.py
│ ├── cleaning.py
│ └── validation.py
│
├── analysis/
│ ├── statistics.py
│ └── metrics.py
│
├── visualization/
│ ├── charts.py
│ └── dashboards.py
│
└── main.py
Here, data, analysis, and visualization can represent separate logical areas of the application.
The project is easier to understand because the folder structure communicates how the application is organized.
Consider a project containing 30 Python modules.
Without logical grouping:
project/
│
├── loader.py
├── cleaner.py
├── validator.py
├── statistics.py
├── metrics.py
├── chart.py
├── dashboard.py
├── report.py
├── email.py
├── database.py
├── api.py
├── config.py
└── ...
As the project grows, finding the correct file becomes harder.
With packages:
project/
│
├── data/
│ ├── loader.py
│ ├── cleaner.py
│ └── validator.py
│
├── analytics/
│ ├── statistics.py
│ └── metrics.py
│
├── visualization/
│ ├── chart.py
│ └── dashboard.py
│
├── reporting/
│ └── report.py
│
└── main.py
The structure immediately communicates where different functionality belongs.
Another benefit of packages is that they help organize names.
Imagine two modules with the same filename:
sales.py
One could belong to a sales package:
business/sales.py
while another could belong to an analytics package:
analytics/sales.py
The package path helps distinguish them conceptually:
business.sales
analytics.sales
This type of organization becomes increasingly useful in larger applications.
Just as with modules, packages should have clear names.
Good examples include:
analytics
data
reports
visualization
database
utilities
A package name should generally communicate what functionality it contains.
For example:
data/
is much easier to understand than:
abc/
when the package contains data-related functionality.
Packages can also be organized into multiple levels.
For example:
project/
│
└── analytics/
│
├── data/
│ ├── loader.py
│ └── cleaning.py
│
├── statistics/
│ └── descriptive.py
│
└── visualization/
└── charts.py
This gives us a hierarchy:
analytics
↓
data
↓
loader.py
Such structures are useful in larger applications, although beginners should avoid creating unnecessary levels of folders. The goal is organization, not complexity.
Another term you will frequently hear in Python is library.
A package and a library are related concepts, but they are not exactly the same thing.
A package is a structural way of organizing Python modules.
A library generally refers to reusable software functionality that developers can use in their applications. A library may contain one or more packages and modules.
For example, when working with Data Analytics, you will encounter libraries such as Pandas and NumPy. These provide extensive reusable functionality for data manipulation, numerical computing, and related tasks.
The important beginner-level idea is:
Module
↓
One Python file
Package
↓
Organized collection of modules
Library
↓
Reusable collection of functionality
These terms can sometimes be used loosely in everyday Python discussions, so focus first on the structural concept of modules and packages.
You do not need to create a package for every small Python program.
For example, a simple script containing 30 lines of code probably does not need a complicated package structure.
Packages become more useful when:
A good rule is to introduce structure when the project actually needs it.
Let’s create a small analytics package.
Project structure:
student_project/
│
├── main.py
│
└── analytics/
├── statistics.py
└── grading.py
Inside statistics.py:
def average(values):
if not values:
return 0
return sum(values) / len(values)
def highest(values):
if not values:
return None
return max(values)
Inside grading.py:
def get_grade(marks):
if marks >= 90:
return "A+"
elif marks >= 80:
return "A"
elif marks >= 70:
return "B"
elif marks >= 60:
return "C"
elif marks >= 40:
return "D"
return "F"
Now main.py can import both modules:
from analytics import statistics
from analytics import grading
Then:
marks = [
85,
92,
76,
88
]
print(
statistics.average(marks)
)
print(
statistics.highest(marks)
)
print(
grading.get_grade(85)
)
This is a simple but realistic example of using a package to organize related modules.
When you start working with professional Python applications, you will rarely see every piece of functionality placed into one file.
Projects are usually divided into logical components.
For example:
application/
│
├── authentication/
├── database/
├── analytics/
├── reports/
├── api/
├── configuration/
└── main.py
Each area can contain several modules.
This makes the project easier to navigate and maintain.
For a Data Analytics application, the same principle could be applied to:
data/
analysis/
visualization/
reporting/
automation/
This is one reason understanding Python packages is important before moving into larger Python projects and external libraries.
Create the following project:
analytics_project/
│
├── main.py
│
└── analytics/
├── calculations.py
└── grading.py
Inside calculations.py, create:
total()
average()
maximum()
minimum()
Inside grading.py, create:
get_grade()
Then import these modules from main.py.
Test them with:
marks = [
75,
82,
91,
68,
88
]
Your main program should display:
This exercise will help you understand how multiple modules can be grouped into one logical package.
from package import module.from package.module import function.In the next part, we will focus on the __init__.py file, how it is used inside Python packages, how package imports can be organized, and how a package can expose selected functionality to the rest of an application.
In the previous part, we learned that a Python package provides a way to organize related modules inside a directory. Now we will look at one of the most important files associated with traditional Python package structures: __init__.py.
The file name is written with two underscores before and after init:
__init__.py
It is easy to confuse this with:
init.py
but they are different filenames. The correct Python filename is __init__.py.
Historically, placing an __init__.py file inside a directory was the standard way to make that directory behave as a regular Python package. Modern Python also supports namespace packages without an __init__.py in some situations, but understanding __init__.py remains important because it is still widely used for package initialization, organization, and controlling package-level imports.
Let’s create a simple package.
my_project/
│
├── main.py
│
└── analytics/
├── __init__.py
├── statistics.py
└── grading.py
Here:
analytics/
is the package directory.
The file:
__init__.py
is the package initialization file.
And:
statistics.py
grading.py
are modules inside the package.
The simplest approach is to create an empty __init__.py file.
analytics/
│
├── __init__.py
├── statistics.py
└── grading.py
The __init__.py file does not have to contain code.
An empty file can still serve as the package’s initialization module.
For a beginner project, this is often enough.
Then from main.py, you can import modules from the package:
from analytics import statistics
from analytics import grading
And use them:
marks = [
80,
90,
75,
88
]
print(
statistics.average(marks)
)
print(
grading.get_grade(85)
)
This gives us the basic structure:
Package
↓
analytics
↓
__init__.py
↓
Package initialization
statistics.py
↓
Statistics functionality
grading.py
↓
Grading functionality
The __init__.py file can be used for several purposes.
You do not need to put complicated code into __init__.py. In many packages, it may remain very small or even empty.
We can put simple information inside __init__.py.
"""
Analytics package for basic data analysis.
"""
PACKAGE_NAME = "Analytics Tools"
PACKAGE_VERSION = "1.0"
Now another program can access these package-level variables:
import analytics
print(
analytics.PACKAGE_NAME
)
print(
analytics.PACKAGE_VERSION
)
This demonstrates that __init__.py can provide objects at the package level.
One useful feature of __init__.py is that it can expose selected functions from modules inside the package.
Suppose:
analytics/
│
├── __init__.py
├── statistics.py
└── grading.py
Inside statistics.py:
def average(values):
if not values:
return 0
return sum(values) / len(values)
def maximum(values):
if not values:
return None
return max(values)
Normally, you could import the function using:
from analytics.statistics import average
But we can also expose average() through __init__.py.
Inside analytics/__init__.py:
from .statistics import average
The dot means that the import is relative to the current package.
Now we can write:
from analytics import average
instead of:
from analytics.statistics import average
This can make the public interface of a package simpler.
The following syntax:
from .statistics import average
uses a relative import.
The dot means:
current package
So:
from .statistics import average
means that Python should find the statistics module relative to the current package.
Similarly, if a package contains another subpackage, you may see multiple dots in relative import syntax.
For basic Python development, the most important thing to remember is:
.
↓
Current package
When a package becomes larger, you may not want users to remember the exact location of every function.
For example, without package-level imports:
from analytics.statistics import average
from analytics.statistics import maximum
from analytics.grading import get_grade
With carefully designed __init__.py, you could expose commonly used functions:
from .statistics import average, maximum
from .grading import get_grade
Now users can write:
from analytics import (
average,
maximum,
get_grade
)
This creates a cleaner package interface.
The package acts as a convenient entry point to selected functionality.
Although __init__.py can contain code, it is usually better to keep it simple.
A beginner might be tempted to put all application logic inside it.
For example, avoid turning __init__.py into a large file containing:
Data loading
Database connections
Complex calculations
Reports
API calls
Application execution
Instead, keep those responsibilities in appropriate modules.
For example:
analytics/
│
├── __init__.py
├── data_loader.py
├── statistics.py
├── validation.py
└── reporting.py
The initialization file should generally help organize the package rather than become another large application file.
Code inside __init__.py can execute when the package is imported.
For example:
print("Analytics package initialized")
Then:
import analytics
can cause that statement to execute.
This demonstrates why you should avoid putting unnecessary side effects inside __init__.py.
If importing a package unexpectedly starts a process, opens a connection, or performs expensive operations, the package can become difficult to use.
For basic packages, it is usually better to keep initialization lightweight.
Package-level constants can also be defined there.
For example:
PACKAGE_NAME = "Student Analytics"
PACKAGE_VERSION = "1.0.0"
Then:
import analytics
print(
analytics.PACKAGE_VERSION
)
This is a simple use case for package metadata.
Let’s build a small Data Analytics package.
analytics/
│
├── __init__.py
├── statistics.py
├── cleaning.py
└── validation.py
The statistics.py module might contain:
def average(values):
if not values:
return 0
return sum(values) / len(values)
def maximum(values):
if not values:
return None
return max(values)
The cleaning.py module might contain:
def remove_empty(values):
return [
value
for value in values
if value is not None
]
The validation.py module might contain:
def is_numeric(value):
return isinstance(
value,
(int, float)
)
Now __init__.py can expose commonly used functionality:
from .statistics import average, maximum
from .cleaning import remove_empty
from .validation import is_numeric
Now the package can be used like:
from analytics import (
average,
maximum,
remove_empty,
is_numeric
)
This creates a simple and convenient package interface.
The syntax:
from .statistics import average
can look unusual when you first see it.
The leading dot indicates a relative import.
It means:
Look inside the current package.
Therefore:
from .statistics import average
means:
Current Package
↓
statistics.py
↓
average()
This is different from an absolute import such as:
from analytics.statistics import average
Both approaches can access the same function, but they express the relationship differently.
Let’s compare the two approaches.
Without exposing the function through __init__.py:
from analytics.statistics import average
With the function exposed through __init__.py:
from analytics import average
The second version can be simpler for users of the package.
This is one reason package authors may use __init__.py to create a clean public interface.
Create the following project:
student_project/
│
├── main.py
│
└── student_tools/
├── __init__.py
├── statistics.py
└── grading.py
Inside statistics.py, create:
average()
highest()
lowest()
Inside grading.py, create:
get_grade()
Then inside __init__.py, expose the functions:
from .statistics import (
average,
highest,
lowest
)
from .grading import get_grade
Finally, in main.py:
from student_tools import (
average,
highest,
lowest,
get_grade
)
Test the package using:
marks = [
75,
82,
91,
68,
88
]
Display the average, highest marks, lowest marks, and grade.
You may encounter Python projects where a directory does not contain an __init__.py file and Python still allows it to participate in imports. This is possible because modern Python supports namespace packages.
Therefore, it is not always correct to say that every importable package must contain __init__.py.
For this course, remember the practical distinction:
__init__.py
↓
Traditional and widely used package initialization file
It can be empty, or it can contain package-level code and selected imports.
Modern Python can also create namespace packages without it in appropriate circumstances.
__init__.py is a special file commonly used inside Python packages.from .statistics import average.__init__.py simple generally makes packages easier to maintain.__init__.py in every case.__init__.py makes it easier to work with larger Python projects and external packages.In the next part, we will learn how to import modules and functions from packages in different ways, including absolute imports, relative imports, subpackages, aliases, and practical project organization.
Once you understand Python packages and the purpose of __init__.py, the next important skill is learning how to import modules and functions from a package correctly. In real Python projects, you will frequently work with multiple folders, modules, subpackages, and reusable functions.
The basic import patterns you should understand are:
import package.module
from package import module
from package.module import function
import package.module as alias
from package.module import function as alias
These patterns allow you to access functionality without putting all of your Python code into a single file.
Consider this project structure:
my_project/
│
├── main.py
│
└── analytics/
├── __init__.py
├── statistics.py
└── grading.py
Suppose statistics.py contains:
def average(values):
if not values:
return 0
return sum(values) / len(values)
def maximum(values):
if not values:
return None
return max(values)
From main.py, we can import the module using:
from analytics import statistics
Then access its functions using:
marks = [
75,
82,
91,
68,
88
]
print(
statistics.average(marks)
)
print(
statistics.maximum(marks)
)
Here, analytics is the package and statistics is the module inside that package.
The structure is:
analytics
↓
statistics
↓
average()
maximum()
Another approach is to import the module using its complete path:
import analytics.statistics
Then use:
analytics.statistics.average(marks)
This makes the complete location of the function visible.
For example:
import analytics.statistics
marks = [
70,
80,
90
]
result = analytics.statistics.average(
marks
)
print(result)
This approach can be useful when you want the package hierarchy to remain explicit.
If you only need one function, you can import that function directly.
from analytics.statistics import average
Now you can write:
marks = [
70,
80,
90
]
result = average(marks)
print(result)
There is no need to write:
analytics.statistics.average()
because average() has been imported directly into the current namespace.
You can import several functions from the same module:
from analytics.statistics import (
average,
maximum
)
Then:
marks = [
70,
80,
90
]
print(
average(marks)
)
print(
maximum(marks)
)
This is convenient when your program needs several specific functions from one module.
You can give an imported module a shorter local name using as.
import analytics.statistics as stats
Now:
marks = [
70,
80,
90
]
print(
stats.average(marks)
)
print(
stats.maximum(marks)
)
The alias does not change the actual module. It only gives the current program another name through which the module can be accessed.
Aliases can be useful when module paths are long or when a commonly accepted short name improves readability.
Functions can also be imported with aliases.
from analytics.statistics import average as calculate_average
Now use:
marks = [
70,
80,
90
]
print(
calculate_average(marks)
)
This can be useful when the imported name is too generic or conflicts with another name in the program.
An absolute import specifies the package path from the project’s importable top level.
For example:
from analytics.statistics import average
This explicitly identifies:
analytics
↓
statistics
↓
average
Absolute imports are often easy to read because the source of the imported functionality is clear.
Relative imports are commonly used inside packages when one module needs functionality from another module within the same package.
Consider:
analytics/
│
├── __init__.py
├── statistics.py
└── grading.py
Suppose grading.py needs a function from statistics.py.
It can use:
from .statistics import average
The leading dot means:
Current package
↓
statistics.py
Now grading.py can use:
average(marks)
without importing the package using its full external path.
Imagine a package named:
student_analytics
containing:
student_analytics/
│
├── __init__.py
├── statistics.py
└── grading.py
Inside grading.py, we can write:
from .statistics import average
This says that statistics.py belongs to the same package.
If the package is later moved or distributed under a different top-level application, the internal relative relationship remains meaningful.
Relative imports can use more than one dot.
A single dot:
.
means the current package.
Two dots:
..
mean the parent package.
For example:
from .statistics import average
means:
current package → statistics
While:
from ..common import helper
conceptually means:
parent package → common → helper
Relative imports become more useful in larger package hierarchies, although beginners should use them only when the package structure actually requires them.
A package can contain another package. This is called a subpackage.
For example:
project/
│
├── main.py
│
└── analytics/
├── __init__.py
│
├── data/
│ ├── __init__.py
│ ├── loader.py
│ └── cleaning.py
│
└── statistics/
├── __init__.py
└── descriptive.py
Here:
analytics
is the main package.
And:
analytics.data
and:
analytics.statistics
are subpackages.
A function from loader.py could be imported using:
from analytics.data.loader import load_data
This path tells Python exactly where the function is located.
When dealing with packages, think of the import path as a hierarchy.
analytics.data.loader.load_data
can be understood as:
analytics
↓
data
↓
loader
↓
load_data()
This hierarchical structure is one of the main reasons packages are useful for large Python projects.
Earlier, we learned that __init__.py can expose selected functionality.
Suppose:
analytics/
│
├── __init__.py
└── statistics.py
And statistics.py contains:
def average(values):
if not values:
return 0
return sum(values) / len(values)
Inside analytics/__init__.py:
from .statistics import average
Now the user can simply write:
from analytics import average
instead of:
from analytics.statistics import average
This creates a simpler package interface.
A package may contain many internal functions, but not every function needs to be exposed directly at the package level.
For example:
statistics.py
def average(values):
...
def maximum(values):
...
def _validate_values(values):
...
You may choose to expose:
average
maximum
while keeping internal helper functions within their module.
Then __init__.py could contain:
from .statistics import average, maximum
This gives users a cleaner interface.
A real Python application can use several packages.
For example:
from analytics.statistics import average
from analytics.visualization import create_chart
from analytics.reporting import generate_report
Each import identifies functionality from a different module within the project.
This type of organization is common in larger applications.
Let’s create a small package structure for a Data Analytics workflow.
analytics_project/
│
├── main.py
│
└── analytics/
├── __init__.py
│
├── data/
│ ├── __init__.py
│ └── loader.py
│
├── analysis/
│ ├── __init__.py
│ └── statistics.py
│
└── reporting/
├── __init__.py
└── report.py
The data loader might contain:
def load_sales():
return [
10000,
15000,
18000,
12000,
22000
]
The statistics module:
def average(values):
if not values:
return 0
return sum(values) / len(values)
def highest(values):
if not values:
return None
return max(values)
The report module:
def display_report(
average_value,
highest_value
):
print(
"Average Sales:",
round(average_value, 2)
)
print(
"Highest Sales:",
highest_value
)
Then main.py can connect everything:
from analytics.data.loader import load_sales
from analytics.analysis.statistics import (
average,
highest
)
from analytics.reporting.report import (
display_report
)
def run():
sales = load_sales()
average_value = average(
sales
)
highest_value = highest(
sales
)
display_report(
average_value,
highest_value
)
if __name__ == "__main__":
run()
Notice how main.py contains the workflow, while individual responsibilities are stored in separate modules.
When working with packages, beginners often encounter import errors.
ModuleNotFoundError may occur when Python cannot locate a package or module.
from analytics.statistics import average
If Python cannot find the expected analytics package, the import will fail.
Another problem can occur when the module exists but the requested function does not.
from analytics.statistics import average_sales
when the actual function is:
average()
This can lead to an import-related error.
Suppose two modules contain a function called calculate_total().
For example:
sales.py
student.py
Both contain:
calculate_total()
Importing both directly can create a naming conflict.
Instead, import the modules:
from analytics import sales
from analytics import student
Then:
sales.calculate_total()
student.calculate_total()
This keeps the source of each function clear.
Another approach is aliases:
from analytics.sales import (
calculate_total as sales_total
)
from analytics.student import (
calculate_total as student_total
)
Now:
sales_total()
student_total()
This makes the difference explicit.
Let’s summarize the difference.
| Import Type | Example | Meaning |
|---|---|---|
| Module import | import analytics.statistics |
Import a module using its package path |
| Package module | from analytics import statistics |
Import a module from a package |
| Function import | from analytics.statistics import average |
Import a specific function |
| Relative import | from .statistics import average |
Import from the current package |
| Alias | import analytics.statistics as stats |
Give an imported module another local name |
Create this project:
sales_app/
│
├── main.py
│
└── sales/
├── __init__.py
├── data.py
├── analysis.py
└── report.py
In data.py, create a sales list.
In analysis.py, create:
total()
average()
maximum()
minimum()
In report.py, create a function that displays the results.
Then import the required functionality into main.py and create a complete sales analysis workflow.
Try both styles:
from sales.analysis import average
and:
from sales import analysis
analysis.average(data)
Observe how the two approaches differ.
__init__.py can expose selected functionality at the package level.With modules, packages, imports, and __init__.py understood, you now have the core foundation required to organize Python projects beyond a single file. The next part will bring these concepts together in a practical package-based project with exercises, common mistakes, interview questions, and revision.
अब तक आपने Python packages, __init__.py, absolute imports, relative imports, aliases और package structure समझ लिया है। अब इन concepts को एक practical project में combine करते हैं ताकि आपको केवल syntax नहीं, बल्कि complete project organization भी समझ आए।
हम एक छोटा Sales Analytics Package बनाएंगे। इसमें data, calculations और reporting को अलग-अलग modules में रखा जाएगा। यह structure आगे Data Analytics projects में बहुत useful होगा।
सबसे पहले एक folder बनाइए:
sales_project/
इसके अंदर structure बनाएं:
sales_project/
│
├── main.py
│
└── sales/
├── __init__.py
├── data.py
├── analysis.py
└── report.py
यहाँ sales हमारा Python package है और इसके अंदर तीन modules हैं:
data.py — sales dataanalysis.py — calculationsreport.py — report generationsales/data.py में sales data रखें:
sales = [
10000,
15000,
18000,
12000,
22000,
25000,
17000
]
यह module केवल data provide कर रहा है। बाद में इसी जगह CSV या database से data load करने की functionality भी रखी जा सकती है।
अब sales/analysis.py बनाइए:
def calculate_total(values):
return sum(values)
def calculate_average(values):
if not values:
return 0
return sum(values) / len(values)
def find_highest(values):
if not values:
return None
return max(values)
def find_lowest(values):
if not values:
return None
return min(values)
यह module केवल analysis से संबंधित functionality रखता है।
अब sales/report.py बनाइए:
def display_report(
total,
average,
highest,
lowest
):
print(
"\n--- Sales Report ---"
)
print(
"Total Sales:",
total
)
print(
"Average Sales:",
round(average, 2)
)
print(
"Highest Sale:",
highest
)
print(
"Lowest Sale:",
lowest
)
अब report generation भी अलग module में है।
अब हम package के __init__.py का उपयोग करेंगे।
File:
sales/__init__.py
इसमें commonly used functionality expose कर सकते हैं:
from .data import sales
from .analysis import (
calculate_total,
calculate_average,
find_highest,
find_lowest
)
from .report import display_report
अब package के बाहर से हमें हर module का पूरा path लिखने की जरूरत नहीं होगी।
हम सीधे लिख सकते हैं:
from sales import (
sales,
calculate_total,
calculate_average,
find_highest,
find_lowest,
display_report
)
यह __init__.py का practical फायदा है। यह package के commonly used components के लिए एक convenient interface बना सकता है।
अब main.py में सभी functionality import करें:
from sales import (
sales,
calculate_total,
calculate_average,
find_highest,
find_lowest,
display_report
)
def run():
total = calculate_total(
sales
)
average = calculate_average(
sales
)
highest = find_highest(
sales
)
lowest = find_lowest(
sales
)
display_report(
total,
average,
highest,
lowest
)
if __name__ == "__main__":
run()
अब project को main.py से run करें।
Application का flow होगा:
sales/data.py
↓
Sales Data
↓
analysis.py
↓
Calculations
↓
report.py
↓
Sales Report
↓
main.py
↓
Complete Application
अब पूरे project को एक साथ देखें:
sales_project/
│
├── main.py
│
└── sales/
│
├── __init__.py
│
├── data.py
│
├── analysis.py
│
└── report.py
यहाँ package ने तीन modules को logically group किया है।
main.py को यह जानने की जरूरत नहीं है कि calculation किस file में है। वह package से required functionality import कर रहा है।
हम वही project बिना __init__.py के exposed imports का उपयोग किए भी चला सकते हैं:
from sales.data import sales
from sales.analysis import (
calculate_total,
calculate_average,
find_highest,
find_lowest
)
from sales.report import display_report
यह भी valid import style है।
लेकिन यदि package के commonly used functions को __init__.py में expose किया गया है, तो:
from sales import calculate_average
जैसा import अधिक convenient हो सकता है।
आप चाहें तो modules को import करके भी काम कर सकते हैं:
from sales import data
from sales import analysis
from sales import report
अब:
total = analysis.calculate_total(
data.sales
)
average = analysis.calculate_average(
data.sales
)
highest = analysis.find_highest(
data.sales
)
lowest = analysis.find_lowest(
data.sales
)
report.display_report(
total,
average,
highest,
lowest
)
यह style package hierarchy को अधिक स्पष्ट बनाती है।
अब project को थोड़ा बेहतर बनाते हैं। एक नया module बनाइए:
sales/validation.py
इसमें:
def validate_sales(values):
if not values:
return False
for value in values:
if not isinstance(
value,
(int, float)
):
return False
if value < 0:
return False
return True
अब __init__.py में इसे expose कर सकते हैं:
from .validation import validate_sales
अब main.py में:
from sales import validate_sales
और analysis से पहले validation करें:
if not validate_sales(sales):
print(
"Invalid sales data."
)
return
अब workflow अधिक realistic हो गया:
Load
↓
Validate
↓
Analyze
↓
Report
Package में configuration values भी रखी जा सकती हैं। उदाहरण के लिए __init__.py में:
PACKAGE_VERSION = "1.0.0"
अब:
from sales import PACKAGE_VERSION
print(PACKAGE_VERSION)
इस प्रकार package-level information भी उपलब्ध कराई जा सकती है।
अब package के अंदर relative imports का example देखते हैं।
मान लीजिए report.py को data.py से कुछ functionality चाहिए। Package के अंदर हम लिख सकते हैं:
from .data import sales
यहाँ . current package यानी sales को represent करता है।
Similarly, analysis.py भी package के दूसरे module से functionality import कर सकता है:
from .data import sales
लेकिन unnecessary imports से बचना चाहिए। हर module को केवल वही dependency import करनी चाहिए जिसकी उसे वास्तव में आवश्यकता है।
1. Wrong package path
अगर structure है:
sales/
analysis.py
तो गलत import हो सकता है:
from analytics.analysis import calculate_total
जब actual package का नाम sales है।
सही:
from sales.analysis import calculate_total
2. Wrong function name
अगर module में function है:
calculate_average()
तो:
from sales.analysis import average
काम नहीं करेगा।
3. Circular imports
अगर:
module_a.py
↓
imports module_b.py
module_b.py
↓
imports module_a.py
तो unnecessary circular dependency create हो सकती है। Package design करते समय dependencies को simple रखना बेहतर है।
4. Too much code in __init__.py
__init__.py को बहुत बड़ा application file बनाने से बचें। इसका उद्देश्य package organization और convenient package-level access में मदद करना है।
5. Too many packages
हर छोटी functionality के लिए अलग package बनाना जरूरी नहीं है। Package structure तभी बनाएं जब वह project को वास्तव में अधिक organized बनाता हो।
अब अपना दूसरा package बनाइए:
student_project/
│
├── main.py
│
└── student/
├── __init__.py
├── data.py
├── statistics.py
├── grading.py
└── validation.py
data.py में students रखें:
students = [
{
"name": "Rahul",
"marks": 85
},
{
"name": "Priya",
"marks": 92
},
{
"name": "Amit",
"marks": 76
}
]
statistics.py में functions बनाएं:
average()
highest()
lowest()
grading.py में:
get_grade()
validation.py में:
validate_student()
और __init__.py से important functions expose करें।
अंत में main.py से complete student analysis चलाएं।
Question 1: Python package क्या होता है?
अपने शब्दों में explain करें कि package और module में क्या difference है।
Question 2: __init__.py का purpose क्या है?
इसके कम से कम दो practical uses बताइए।
Question 3: इन दोनों imports में क्या difference है?
from sales import analysis
from sales.analysis import calculate_average
Question 4: Relative import में dot का क्या मतलब है?
from .statistics import average
Question 5: Alias क्यों इस्तेमाल किया जाता है?
import sales.analysis as analysis
What is a Python package?
A Python package is a way of organizing related Python modules into a directory structure.
What is __init__.py?
It is a special Python file commonly used inside packages. It can initialize the package, define package-level objects, and expose selected functionality.
Is __init__.py always required for a Python package?
No. Modern Python supports namespace packages that can work without an __init__.py in appropriate situations. However, __init__.py remains widely used and is important to understand.
What is a relative import?
A relative import refers to modules based on their location within the current package hierarchy.
What does the dot mean in a relative import?
A single dot generally refers to the current package.
Can a package contain subpackages?
Yes. A package can contain additional package directories, creating a hierarchical project structure.
Why use packages in large Python projects?
Packages help organize related modules, improve maintainability, reduce clutter, and make larger projects easier to navigate.
अब पूरे topic को एक simple hierarchy से समझें:
Python Project
↓
Package
↓
Modules
↓
Functions / Classes / Variables
एक example:
analytics_project/
│
├── main.py
│
└── analytics/
│
├── __init__.py
│
├── data.py
│
├── analysis.py
│
└── report.py
Import के common methods:
import analytics.analysis
from analytics import analysis
from analytics.analysis import calculate_average
import analytics.analysis as stats
Package के अंदर relative import:
from .analysis import calculate_average
और package-level import के लिए __init__.py में:
from .analysis import calculate_average
इसके बाद:
from analytics import calculate_average
लिखना possible हो सकता है।
सबसे important बात यह है कि packages का उद्देश्य project को unnecessarily complicated बनाना नहीं है। उनका उद्देश्य related functionality को logical और manageable तरीके से organize करना है।
एक छोटे Python script के लिए आपको package structure की जरूरत नहीं हो सकती। लेकिन जैसे-जैसे आपका project बड़ा होता है, modules और packages code को व्यवस्थित रखने में महत्वपूर्ण भूमिका निभाते हैं।
__init__.py का basic purpose समझना.__init__.py के माध्यम से selected functionality expose करना.Python Modules & Packages lesson अब complete है.