Modules and Packages in Python
Table of Contents
- Introduction
- What is a Module?
- Why Use Modules?
- Types of Modules
- Importing Modules
- Understanding the dir() Function in Python
- Executing a Module as a Script
- Built-in Modules Overview
- What is a Package?
- Importing from a Package
- The init.py File
- Namespace and Scope in Modules
- The sys.path and Module Search Path
- Installing and Using Third-party Packages
- Creating Your Own Package and Publishing It
- Difference Between Module and Package
- Example: Real-world Package Structure
- Summary
- Conclusion
1. Introduction
As Python programs grow larger, putting all code in a single file becomes unmanageable.
To organize and reuse code efficiently, Python provides modules and packages.
Both are ways to structure your code logically, improve reusability, and make it maintainable.
2. What is a Module?
A module is simply a Python file (.py) that contains code — functions, variables, and classes — which you can import and use in another Python program.
It helps you group related functionality together.
Example:
Let’s say you create a file named math_utils.py:
# math_utils.py
def add(a, b):
return a + b
def subtract(a, b):
return a - b
PI = 3.14159
Now you can use this file in another script:
# main.py
import math_utils
print(math_utils.add(5, 3))
print(math_utils.PI)
Output:
8
3.14159
3. Why Use Modules?
✅ Organization – Divide large programs into smaller, logical files.
✅ Reusability – Code once, reuse across projects.
✅ Namespace management – Avoid variable conflicts.
✅ Maintainability – Easier to update and debug.
4. Types of Modules
Python provides three categories of modules:
| Type | Description | Example |
|---|---|---|
| Built-in Modules | Pre-installed with Python | math, os, sys, random, datetime |
| User-defined Modules | Created by you | math_utils.py |
| Third-party Modules | Installed using pip | numpy, pandas, requests |
5. Importing Modules
You can use the import statement in various ways.
1️⃣ Import Entire Module
import math
print(math.sqrt(16))
print(math.pi)
2️⃣ Import Specific Items
from math import sqrt, pi
print(sqrt(25))
print(pi)
3️⃣ Import All (Not Recommended)
from math import *
print(sin(0))
This imports everything from the module, but can lead to naming conflicts, so it’s discouraged.
4️⃣ Import with Alias
import math as m
print(m.factorial(5))
This is useful for shorter names or avoiding naming conflicts.
Understanding the dir() Function in Python
In Python, introspection—the ability to examine objects at runtime—is one of its most powerful features. Among the built-in tools that make introspection simple and effective, the dir() function stands out as a quick way to explore what attributes, methods, and properties an object contains. Whether you’re learning a new library, debugging your code, or simply curious about what’s happening behind the scenes, dir() can be your best friend.
What Is the dir() Function?
The dir() function is a built-in Python method that returns a sorted list of all attributes (names of variables, functions, classes, and methods) defined within an object, module, or current scope. In other words, it helps you find out what names are defined inside something.
It is most commonly used to:
- Explore built-in modules such as math, random, or os
- Inspect custom classes or objects
- View all names currently available in your Python environment
Syntax of dir()
dir([object])
The object parameter is optional:
- If no object is passed, dir() lists all names defined in the current local scope.
- If an object is passed, dir() lists all attributes associated with that object.
How dir() Works
When you call dir() on an object, Python looks into its namespace and returns a list of all the names that the object defines.
These names can represent:
- Data attributes (variables)
- Methods (functions inside a class)
- Special methods (also known as “dunder” methods, like __init__, __str__, etc.)
For example, every Python object has some default built-in methods. When you call dir() on that object, you’ll see both its user-defined and default attributes.
Using dir() Without Arguments
If you use dir() without passing anything, it will show you all the names that exist in the current local scope.
This includes all variables, functions, classes, and imported modules that are currently defined.
a = 10
b = "Hello"
def greet():
print("Hi!")
print(dir())
Output (partial):
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'a', 'b', 'greet']
This can be particularly useful when you want to know what’s currently available in your working environment.
Using dir() With a Module
When dir() is used with a module, it lists all the functions, constants, and variables that are defined inside that module.
For example:
import math
print(dir(math))
Output (partial):
['acos', 'asin', 'atan', 'ceil', 'cos', 'degrees', 'e', 'exp', 'factorial', 'pi', 'sin', 'sqrt']
Here, dir(math) displays all the mathematical constants (like pi, e) and functions (like sqrt(), sin(), cos(), etc.) available in the math module.
This makes it a great way to discover what a module can do without opening its documentation.
Using dir() With Objects
The dir() function is not limited to modules. You can also use it to explore any object in Python, including strings, lists, and user-defined classes.
Example 1: On a String
s = "Python"
print(dir(s))
Output (partial):
['capitalize', 'casefold', 'center', 'count', 'encode', 'endswith',
'find', 'format', 'isalnum', 'isalpha', 'replace', 'split', 'upper']
Here, the output lists all string methods that can be used with s. This is helpful for discovering new operations you can perform on strings.
Example 2: On a List
numbers = [1, 2, 3]
print(dir(numbers))
Output (partial):
['append', 'clear', 'copy', 'count', 'extend', 'index',
'insert', 'pop', 'remove', 'reverse', 'sort']
These are the built-in methods available for list manipulation. With dir(), you can instantly see which operations are supported.
Using dir() With Custom Classes
When used on a user-defined class, dir() shows all the attributes that belong to it — including default special methods and custom-defined ones.
class Student:
def __init__(self, name):
self.name = name
def greet(self):
return "Hello " + self.name
print(dir(Student))
Output (partial):
['__class__', '__delattr__', '__dict__', '__doc__', '__init__', '__module__', 'greet']
Notice how Python automatically adds several built-in methods (those starting and ending with double underscores) in addition to the user-defined method greet().
Practical Uses of dir()
The dir() function is more than a curiosity tool — it’s widely used in real-world scenarios, especially when:
- Exploring unfamiliar modules: You can discover what functions are available without searching documentation.
- Debugging: When you’re unsure why an attribute isn’t working, dir() can show what’s actually available.
- Learning: You can call dir() on any built-in type (str, list, dict) to see all its methods.
- Preventing errors: If you get an AttributeError, you can check with dir() whether the method or property exists.
Example:
text = "Hello"
print(dir(text))
# Suppose you tried text.trim() earlier — you’ll realize Python uses strip(), not trim().
Combining dir() with help()
While dir() lists what exists, the help() function explains what each item does.
They work best together:
import math
print(dir(math)) # See what’s inside
help(math.sqrt) # Learn what sqrt() does
With these two tools, you can explore and understand almost any Python library interactively.
7. Executing a Module as a Script
Every Python file has a special variable __name__.
When a module is run directly, __name__ is set to "__main__".
When imported, it takes the module name.
Example:
# demo_module.py
def greet():
print("Hello from module")
if __name__ == "__main__":
print("Running directly")
greet()
else:
print("Module imported")
If you run python demo_module.py → Output:
Running directly Hello from moduleIf you import it:
import demo_moduleOutput:
Module imported
This is used to prevent unintended code execution when importing modules.
8. Built-in Modules Overview
Python provides a vast standard library of built-in modules. Some commonly used ones:
| Module | Purpose | Example |
|---|---|---|
| math | Mathematical functions | math.sqrt(), math.sin() |
| random | Random number generation | random.randint(1,10) |
| os | Interact with operating system | os.listdir(), os.getcwd() |
| sys | System-specific parameters | sys.version, sys.exit() |
| datetime | Date & time manipulation | datetime.datetime.now() |
| json | JSON parsing | json.dumps(), json.loads() |
| re | Regular expressions | re.match(), re.findall() |
| platform | Info about system/platform | platform.system() |
Example: Using os Module
import os
print(os.getcwd()) # current working directory
print(os.listdir()) # list files in directory
Example: Using random Module
import random
print(random.randint(1, 10))
print(random.choice(['apple', 'banana', 'mango']))
9. What is a Package?
A package is a collection of modules organized in directories.
It helps structure large applications logically.
Technically, a package is a folder that contains an __init__.py file, which tells Python that the directory is a package.
Folder Structure:
my_package/
__init__.py
math_utils.py
string_utils.py
- __init__.py may be empty or can include initialization code.
- The folder my_package is now a package containing two modules.
Example:
File: math_utils.py
def add(a, b):
return a + b
File: string_utils.py
def to_upper(s):
return s.upper()
File: main.py
from my_package import math_utils, string_utils
print(math_utils.add(3, 4))
print(string_utils.to_upper("hello"))
Output:
7
HELLO
10. Importing from a Package
There are multiple ways to import from packages.
Method 1: Import the whole module
import my_package.math_utils
print(my_package.math_utils.add(5, 6))
Method 2: Import a specific function
from my_package.math_utils import add
print(add(5, 6))
Method 3: Import with alias
from my_package import math_utils as m
print(m.add(5, 6))
11. The __init__.py File
This file runs automatically when a package is imported.
Example:
my_package/__init__.py
print("Initializing my_package")
Output when importing:
Initializing my_package
You can also define shortcut imports inside it:
# my_package/__init__.py
from .math_utils import add
from .string_utils import to_upper
Now you can do:
from my_package import add, to_upper
12. Namespace and Scope in Modules —
What It Is:
In Python, a namespace is like a container (or a labeled box) that stores names (identifiers) such as variable names, function names, and class names.
Every module in Python automatically gets its own separate namespace when it is created or imported.
This means that variables, functions, or classes defined in one module do not interfere or conflict with those defined in another. Even if two modules have variables with the same name, they are treated as completely independent because they exist in their own module’s namespace.
Explanation:
When you import a module, Python creates a namespace for it.
You can access its variables or functions using the dot notation (module_name.variable or module_name.function()).
Here is an upgraded, client-ready explanation with function, class, and interface examples — clean, clear, and easy to paste into your document.
Examples
(A) Module-Level Variables: Namespace Isolation
a.py
x = 10
b.py
x = 20
main.py
import a, b
print(a.x) # 10
print(b.x) # 20
Output
10
20
Both modules define x, but since each has its own namespace, there is no conflict.
(B) Function Example (Local vs Module Scope)
calc.py
message = "Module Level"
def show():
local_msg = "Function Level"
print("Inside function:", local_msg)
main.py
import calc
print(calc.message) # Accessible (module scope)
calc.show() # Calls function
# print(calc.local_msg) # ❌ Error — local to function
Here:
- message → global to calc module
- local_msg → only exists inside show() (local scope)
(C) Class Example Inside a Module
shapes.py
pi = 3.14
class Circle:
def __init__(self, r):
self.radius = r
def area(self):
return pi * self.radius * self.radius
main.py
from shapes import Circle
c = Circle(5)
print(c.area()) # Works
Only names from shapes are accessible via its namespace.
(D) Interface Example (Using Abstract Base Classes)
In Python, interfaces are usually created using ABC (Abstract Base Class).
interfaces.py
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
circle.py
from interfaces import Shape
class Circle(Shape):
def __init__(self, r):
self.r = r
def area(self):
return 3.14 * self.r * self.r
main.py
from circle import Circle
c = Circle(4)
print(c.area())
Each file has its own namespace, even though they define related classes.
Why It Is Used / Important:
✅ To prevent naming conflicts between different parts of a program.
✅ To make large programs modular and organized.
✅ To maintain separate versions of variables and functions in different modules.
✅ To make code easier to debug and manage by separating functionality.
Scope Within Modules:
Each variable or function inside a module also has a scope — the region of code where it can be accessed.
- A variable defined inside a function is local to that function.
- A variable defined at the top level of a module is global to that module (but not to other modules).
Thus, namespace defines where a name belongs, and scope defines where that name can be used.
13. The sys.path and Module Search Path —
What It Is:
When you use the import statement in Python, the interpreter needs to know where to find the module file you are trying to import.
Python searches for that module in a specific order, using a list of directories stored in the sys.path variable.
The sys module provides access to system-specific parameters, including the module search path — which tells Python where to look for modules.
Module Search Order:
When Python encounters:
import mymodule
It looks for mymodule.py in the following order:
- Current working directory (the folder from where your script is running).
- Directories listed in sys.path (which includes user-defined and environment paths).
- Standard library directories (where Python’s built-in modules are stored).
- Installed third-party site-packages (modules installed via pip).
If the module isn’t found in any of these locations, Python raises a:
ModuleNotFoundError
Viewing the Module Search Path:
You can view all directories that Python searches through using:
import sys
print(sys.path)
Example Output:
['',
'C:\\Users\\saumya\\AppData\\Local\\Programs\\Python\\Python312\\Lib',
'C:\\Users\\saumya\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages']
This list contains:
- The current directory (''),
- The standard library path,
- The site-packages path for installed libraries.
Adding a Custom Path:
If your module is stored in a different folder (not in the default search locations), you can manually add that directory to sys.path:
import sys
sys.path.append("C:/Users/saumya/my_modules")
Now, if mymodule.py is inside that folder, you can import it normally:
import mymodule
Why It Is Used / Important:
✅ To control where Python searches for modules.
✅ To import custom or user-defined modules stored in different directories.
✅ Useful when working on large projects with multiple folders or packages.
✅ Helps fix “ModuleNotFoundError” by adding the correct search path manually.
14. Installing and Using Third-party Packages
Python uses pip (Python Package Installer) to install external packages.
Example:
pip install requests
Then use it:
import requests
response = requests.get("https://api.github.com")
print(response.status_code)
15. Creating Your Own Package and Publishing It
You can create your own package and upload it to PyPI (Python Package Index).
Basic steps:
- Create your package folder structure.
- Add __init__.py, setup script (setup.py), and metadata.
Build distribution using:
python setup.py sdistUpload with:
twine upload dist/*
This allows others to install your package via pip.
16. Difference Between Module and Package
| Feature | Module | Package |
|---|---|---|
| Definition | A single Python file | A collection of modules in a directory |
| File Type | .py file | Folder with __init__.py |
| Example | math.py | numpy/, my_package/ |
| Import Example | import math | from my_package import module |
17. Example: Real-world Package Structure
ecommerce/
__init__.py
billing/
__init__.py
invoice.py
shipping/
__init__.py
tracking.py
Usage:
from ecommerce.billing import invoice
invoice.generate_invoice()
This structure is exactly how large Python projects (like Django, Flask, etc.) are organized.
18. Summary
| Concept | Description |
|---|---|
| Module | Single .py file containing reusable code |
| Package | Directory containing modules |
| __init__.py | Marks a directory as a package |
| Built-in Module | Comes pre-installed with Python |
| User-defined Module | Created by you |
| Third-party Module | Installed using pip |
| import | Keyword to use modules/packages |
| __name__ == "__main__" | Used to check if file is being run directly |
| sys.path | Defines where Python looks for modules |
19. Conclusion
- Modules help you split your program into multiple files for clarity and reusability.
- Packages allow grouping multiple modules into a structured hierarchy.
- Together, they form the foundation of Python’s modular programming system.
- Every major Python library (like Django, NumPy, or Pandas) is just a collection of packages and modules built on this concept.
Next Blog- File Handling in Python
