Modules & Packages
Organizing code into modules and packages, the different ways to import, __name__ == "__main__", and a practical tour of the Standard Library (math, random, datetime, os, sys, json, re, statistics, collections, itertools, functools).
As programs grow, it doesn't make sense to keep everything in one giant file — and you also don't want to rewrite common functionality (like math operations or date handling) from scratch every time. Python solves this with modules and packages, plus a massive built-in Standard Library of ready-to-use tools.
1. What is a Module?
What is it?
A module is simply a Python file (.py) containing code — variables, functions, classes — that can be reused in other Python files by importing it.
Definition: A module is a single Python file containing reusable code that can be imported into other programs.
Why do we use it?
- Organization — split a large program into logical, manageable files.
- Reusability — write a function once, use it in many different programs.
- Avoid repetition — no need to rewrite common logic (like date calculations) every time.
How does it work?
Any .py file is automatically a module. You bring its contents into another file using the import keyword.
Simple Example — Creating Your Own Module
File: `mymath.py`
pythondef add(a, b): return a + b def subtract(a, b): return a - b
File: `main.py` (in the same folder)
pythonimport mymath print(mymath.add(5, 3)) # 8 print(mymath.subtract(5, 3)) # 2
Explanation of the Code
import mymathloads everything defined insidemymath.py, making it available throughmymath.functionname().- The module name is just the filename, without the
.pyextension.
Common Mistakes
- Trying to import a module that isn't in the same folder (or on Python's search path), causing
ModuleNotFoundError. - Naming your own file the same as a well-known library (e.g., naming your file
random.py), which can conflict with Python's built-in modules.
Important Points
- Any
.pyfile can be treated as a module. - The module name matches the filename (without
.py).
2. Different Ways to Import
Syntax Options
pythonimport math # import the whole module print(math.sqrt(16)) # access with module_name.function() from math import sqrt # import just one function print(sqrt(16)) # use it directly, no prefix needed from math import sqrt, pow # import multiple specific items import math as m # import with an alias (shorter name) print(m.sqrt(16)) from math import * # import EVERYTHING (generally discouraged)
Explanation
import mathkeeps things organized — you always know a function likesqrt()came frommathbecause you writemath.sqrt().from math import sqrtis more convenient when you use a function frequently, but it's less clear where the function came from, especially in larger files.import math as mis common for long library names (very frequently seen with libraries likepandas as pdornumpy as np).
Common Mistakes
- Using
from module import *, which can cause naming conflicts if two modules define something with the same name — it's discouraged in real projects for this reason. - Forgetting to install a third-party module before importing it (built-in modules like
mathneed no installation, but others likerequestsdo — covered in the Package Management file).
Important Points
import module_nameis the safest, clearest default choice.- Aliasing (
as) is a widely used convention for popular libraries.
3. Packages
What is it?
A package is a folder containing multiple related modules, organized together — essentially, a "module of modules."
Definition: A package is a directory containing multiple Python modules, along with a special __init__.py file that marks it as a package.Folder Structure Example
myapp/
__init__.py
calculations.py
formatting.pySimple Example
File: `myapp/calculations.py`
pythondef add(a, b): return a + b
File: `main.py` (outside the myapp folder)
pythonfrom myapp import calculations print(calculations.add(5, 3)) # 8
What is __init__.py?
This special (often empty) file tells Python "this folder is a package, not just a regular folder." Without it, older versions of Python won't recognize the folder as importable (modern Python 3 can sometimes work without it via "namespace packages," but including it is still standard, clear practice).
Real-World Example
Popular libraries like numpy or django are packages — large folders full of organized modules, all accessible through a single import numpy statement.
Common Mistakes
- Forgetting
__init__.pyin a package folder, leading to unexpected import behavior in some setups. - Confusing "module" (single file) with "package" (folder of modules) — a common interview distinction.
Important Points
- A package is a folder; a module is a single file.
__init__.pymarks a folder as a package.
4. __name__ and __main__
What is it?
Every Python file has a built-in variable called __name__. When a file is run directly, __name__ is automatically set to "__main__". When the same file is imported into another file, __name__ is set to the module's actual name instead.
Why do we use it?
This lets you write code in a file that behaves differently depending on whether it's being run directly or just imported for its functions — extremely useful for testing a module's code without that test code running every time someone else imports it.
Simple Example
File: `greetings.py`
pythondef greet(name): print(f"Hello, {name}!") if __name__ == "__main__": greet("Test User") # only runs when this file is executed directly
- Running
python greetings.pydirectly → prints"Hello, Test User!". - Running
import greetingsfrom another file → thegreet()function is available, but"Hello, Test User!"does not print automatically.
Real-World Example
Almost every well-structured Python script or library uses this pattern so that importing it for its functions doesn't accidentally trigger test code or a demo run meant only for direct execution.
Common Mistakes
- Forgetting this check entirely, causing test/demo code to run unexpectedly whenever the file is imported elsewhere.
Important Points
__name__ == "__main__"is a near-universal pattern in real Python projects.- It distinguishes "this file was run directly" from "this file was imported."
Practice
- Create a module with a function and a
if __name__ == "__main__":block that tests the function. Run it directly, then import it from another file and observe the difference.
5. The Python Standard Library — A Practical Tour
Python comes bundled with a huge collection of built-in modules — no installation required. Here are the most commonly used ones.
5.1 math — Mathematical Functions
pythonimport math print(math.sqrt(25)) # 5.0 print(math.pow(2, 3)) # 8.0 print(math.floor(4.7)) # 4 print(math.ceil(4.2)) # 5 print(math.pi) # 3.141592653589793
Real-World Use: Calculating distances, geometry, engineering formulas.
5.2 random — Random Number Generation
pythonimport random print(random.randint(1, 100)) # random integer between 1 and 100 print(random.choice(["A", "B", "C"])) # random item from a list print(random.random()) # random float between 0.0 and 1.0 items = [1, 2, 3, 4, 5] random.shuffle(items) # shuffles the list in place print(items)
Real-World Use: Games (like the Number Guessing Game), generating OTPs, shuffling quiz questions.
5.3 datetime — Dates and Times
pythonfrom datetime import datetime, date now = datetime.now() print(now) # e.g. 2026-09-02 14:30:00.123456 print(now.year, now.month, now.day) birth_date = date(2000, 5, 15) today = date.today() age_days = (today - birth_date).days print(f"You are {age_days} days old")
Real-World Use: Timestamps, age calculators, scheduling systems, log files.
5.4 os — Operating System Interaction
pythonimport os print(os.getcwd()) # current working directory os.mkdir("new_folder") # create a folder print(os.listdir(".")) # list files in current directory print(os.path.exists("main.py")) # check if a file exists
Real-World Use: File automation, checking/creating folders, working with file paths across operating systems.
5.5 sys — System-Specific Parameters
pythonimport sys print(sys.version) # Python version info print(sys.argv) # command-line arguments passed to the script
Real-World Use: Reading command-line arguments, exiting a script early with sys.exit().
5.6 json — Working with JSON Data
pythonimport json data = {"name": "Aditi", "age": 21} json_string = json.dumps(data) # Python dict -> JSON string print(json_string) # {"name": "Aditi", "age": 21} parsed = json.loads(json_string) # JSON string -> Python dict print(parsed["name"]) # Aditi
Real-World Use: APIs almost always send and receive data in JSON format — this module is essential for web development and API work (covered in depth later).
5.7 re — Regular Expressions
pythonimport re text = "Call me at 9876543210" match = re.search(r"\d{10}", text) if match: print("Phone number found:", match.group())
Real-World Use: Validating emails, phone numbers, passwords (covered in full in the Regular Expressions file).
5.8 statistics — Statistical Calculations
pythonimport statistics marks = [85, 90, 78, 92, 88] print(statistics.mean(marks)) # average print(statistics.median(marks)) # middle value print(statistics.mode(marks)) # most common value
Real-World Use: Quick statistical summaries without needing a heavier library like NumPy for simple cases.
5.9 collections — Specialized Data Structures
pythonfrom collections import Counter, defaultdict words = ["apple", "banana", "apple", "cherry", "banana", "apple"] print(Counter(words)) # Counter({'apple': 3, 'banana': 2, 'cherry': 1}) word_count = defaultdict(int) for word in words: word_count[word] += 1 print(dict(word_count))
Explanation: Counter instantly counts occurrences of each item. defaultdict avoids KeyError by providing a default value (here, 0) for keys that don't exist yet.
Real-World Use: Word frequency counters, grouping and tallying data.
5.10 itertools — Efficient Looping Tools
pythonimport itertools # All possible pairs from a list for pair in itertools.combinations([1, 2, 3], 2): print(pair) # (1, 2) # (1, 3) # (2, 3)
Real-World Use: Generating combinations/permutations, efficient looping over large or infinite sequences.
5.11 functools — Functional Programming Tools
pythonfrom functools import reduce, lru_cache total = reduce(lambda a, b: a + b, [1, 2, 3, 4]) print(total) # 10 @lru_cache(maxsize=None) def slow_square(n): return n * n print(slow_square(5)) # cached after first call
Explanation: @lru_cache automatically remembers previous results of a function, so repeated calls with the same input skip re-computation — useful for speeding up expensive functions like recursive calculations.
Real-World Use: Speeding up repeated expensive calculations (like recursive Fibonacci), combining values with reduce.
Comparison Table — Common Standard Library Modules
| Module | Purpose |
|---|---|
math | Mathematical calculations |
random | Random values, shuffling, choices |
datetime | Dates and times |
os | File system and OS interaction |
sys | System/interpreter-level info |
json | Encoding/decoding JSON data |
re | Pattern matching in text |
statistics | Quick statistical calculations |
collections | Specialized containers (Counter, defaultdict) |
itertools | Efficient iteration tools |
functools | Functional programming helpers (reduce, caching) |
Common Beginner Mistakes — Summary
- Using
from module import *, risking naming conflicts. - Naming your own files the same as standard modules (e.g.,
random.py), which shadows the real module. - Forgetting
__init__.pywhen building a package. - Forgetting that
importonly needs to happen once per file, at the top. - Confusing "module" (a file) with "package" (a folder of modules).
Cheat Sheet — Modules & Packages
pythonimport math # standard import from math import sqrt # import specific item import math as m # import with alias from math import * # import everything (avoid in real projects) if __name__ == "__main__": # code that runs only when this file is executed directly pass
myapp/ # package (folder)
__init__.py # marks it as a package
module_one.py # a module inside the package
module_two.pyInterview Questions
Q1. What is the difference between a module and a package? Answer: A module is a single Python file. A package is a folder containing multiple related modules, along with an __init__.py file marking it as a package.
Q2. What does `if __name__ == "__main__":` do? Answer: It checks whether the current file is being run directly (in which case __name__ equals "__main__") rather than imported into another file — code inside this block only runs on direct execution.
*Q3. What is the risk of using `from module import `?** Answer: It can cause naming conflicts if multiple modules define something with the same name, and it makes it unclear where a given function or variable actually came from.
Q4. Name three commonly used Python Standard Library modules and their purpose. Answer: Examples: math (mathematical functions), datetime (dates and times), os (file system and operating system interaction), json (working with JSON data), random (random values).
Q5. What does `__init__.py` do in a package? Answer: It marks a folder as a Python package so its modules can be imported properly.
Practice Questions
Beginner
- Create your own module with two functions and import it into a separate file.
- Use the
mathmodule to calculate the square root and factorial of a number. - Use the
randommodule to simulate rolling a die (random number from 1 to 6). - Use the
datetimemodule to print today's date. - Use the
osmodule to list all files in the current directory.
Intermediate
- Create a package with two modules (
operations.pyandformatting.py) and use both in amain.pyfile. - Use the
jsonmodule to convert a dictionary of student data into a JSON string and back. - Use
collections.Counterto find the most common word in a list of words. - Write a function that uses
if __name__ == "__main__":to test itself only when run directly. - Use the
statisticsmodule to calculate the mean, median, and mode of a list of exam scores.
Challenge
- Use
itertools.permutationsto generate all possible orderings of a 3-letter word. - Build a simple age calculator using the
datetimemodule that takes a birth date and prints the person's exact age in years, months, and days. - Use
functools.lru_cacheto speed up a recursive Fibonacci function, and compare its execution time to the uncached version.