Python tutorials  /  Python Modules and the import Statement
Chapter 2 · Python

Python Modules and the import Statement

A module in Python is simply a file containing Python code (functions, classes, or variables) that can be reused in other Python programs. The 'import' statement is how a Python program gains access to the code defined in another module, whether it's a built-in standard library module, a third-party installed package, or a custom file the developer wrote themselves.

Think of a module like a specific toolbox someone else already built and labeled — say, a 'math toolbox' full of pre-made calculation tools. Instead of building your own calculator functions from scratch every time you need to find a square root, you simply say 'import math' to bring that entire toolbox into your current workspace, and then you can use any tool inside it, like 'math.sqrt()', without ever having to write that logic yourself.

Consider a web scraping script that needs to fetch data from a website, parse dates, and save results to a CSV file. Rather than writing all this functionality from scratch, a developer would 'import requests' (a popular third-party package) to handle the actual web requests, 'import datetime' (a built-in standard library module) to parse and format dates, and 'import csv' (another built-in module) to write the final results to a file. This is exactly how real-world Python scripts are built — by combining small, focused, reusable pieces of code from modules rather than reinventing common functionality every single time.

Without modules and the import system, every single Python program would need to reimplement common functionality (like mathematical operations, date handling, or file reading) completely from scratch, leading to massive code duplication across projects and a much higher chance of bugs in reinvented logic. The import system allows Python's vast standard library and its enormous ecosystem of third-party packages (installable via pip) to be reused instantly in any project, and also allows developers to organize their own larger programs into multiple smaller, well-organized files that can import from one another.

  • Standard Library Modules: Modules that come bundled with every Python installation by default, requiring no additional installation, such as 'math', 'datetime', 'os', 'random', and 'json'.
  • Third-Party Packages: Modules and packages created by the wider Python community, which must be installed separately using a package manager like 'pip' before they can be imported, such as 'requests', 'pandas', or 'numpy'.
  • User-Defined (Custom) Modules: Any Python file (.py) that a developer writes themselves, which can then be imported into other files within the same project, allowing large programs to be organized into smaller, logically separated files.
  • Packages: A way of organizing related modules together into a directory hierarchy, marked by a special '__init__.py' file (in older Python versions), allowing a related collection of modules to be imported together under a shared namespace.
# Import an entire module: import module_name module_name.function_name() # Import a specific item directly: from module_name import function_name function_name() # Import with an alias: import module_name as alias
A beginner wants to write a program that calculates the area of a circle using Python's built-in 'math' module for an accurate value of pi, and also wants to generate a random number using the 'random' module, understanding different ways to import and use these built-in modules.
Importing and Using the Built-in math Module
This example imports Python's standard 'math' module to access a precise value of pi and calculate a circle's area, demonstrating the standard 'import module_name' style.
Python
import math radius = 5 area = math.pi * radius ** 2 print("Value of pi:", math.pi) print("Circle area:", area)
Value of pi: 3.141592653589793 Circle area: 78.53981633974483
'import math' brings the entire 'math' module into the current program, and its contents must be accessed using the 'math.' prefix, such as 'math.pi' for the precise value of pi. The '**' operator performs exponentiation (radius squared), and multiplying this by 'math.pi' calculates the circle's area using the standard formula, demonstrating how a single import gives access to an entire module's functions and constants.
Using from...import and Import Aliasing
This example demonstrates importing a specific function directly using 'from...import' syntax, and using 'as' to give an imported module a shorter alias, both very common real-world patterns.
Python
from random import randint import datetime as dt lucky_number = randint(1, 100) current_year = dt.datetime.now().year print("Your lucky number is:", lucky_number) print("Current year:", current_year)
Your lucky number is: 47 Current year: 2026
'from random import randint' imports ONLY the 'randint' function directly, allowing it to be called simply as 'randint(1, 100)' without needing a 'random.' prefix, unlike a full module import. 'import datetime as dt' imports the entire 'datetime' module but assigns it the shorter alias 'dt', so the module can be referenced as 'dt.datetime.now()' instead of the longer 'datetime.datetime.now()' — this aliasing pattern is extremely common with popular libraries like 'import pandas as pd' or 'import numpy as np' in real-world data science code.
  • Confusing 'import module' with 'from module import *': Using 'from math import *' brings ALL of math's functions and constants directly into the current namespace without any prefix, which can silently overwrite existing variables or functions with the same name and makes it unclear, when reading the code later, exactly where a particular function like 'sqrt()' actually came from. This wildcard import style is generally discouraged in real, production-quality code for exactly this reason.
  • Trying to Import a Third-Party Package Without Installing It First: Writing 'import requests' or 'import pandas' without first installing that package using 'pip install requests' (or 'pip install pandas') results in a 'ModuleNotFoundError: No module named ...'. Beginners often forget that only the STANDARD LIBRARY modules come pre-installed with Python; anything else must be explicitly installed first.
  • Circular Imports Between Custom Modules: If module A imports something from module B, and module B also tries to import something from module A, this creates a circular import dependency that can cause an 'ImportError: cannot import name ...' at runtime, since Python cannot fully finish loading either module before the other one needs it. This typically requires restructuring the code to remove the circular dependency, often by moving shared logic into a third, separate module.
  • Naming a Personal Script the Same as a Standard Library Module: Creating a file named 'math.py' or 'random.py' in the same directory as a script that does 'import math' can cause Python to mistakenly import the developer's own local file instead of the actual standard library module, since Python searches the current directory first. This leads to confusing errors like 'AttributeError: module 'math' has no attribute 'pi'', since the local file doesn't actually contain any of the real math module's functionality.
  • Avoid Wildcard Imports (from module import *): Always prefer explicit imports, either the full 'import module_name' style or a specific 'from module_name import specific_function', rather than 'from module_name import *', to keep it clear exactly which module each function or variable in your code actually came from, and to avoid accidental naming conflicts.
  • Follow Common Aliasing Conventions for Popular Libraries: Use the widely-recognized, community-standard aliases for common data science libraries, such as 'import pandas as pd', 'import numpy as np', and 'import matplotlib.pyplot as plt', making your code instantly familiar and readable to any other Python developer who encounters it.
  • Group and Order Imports at the Top of the File: Place all import statements at the very top of a Python file, conventionally grouped into three sections separated by a blank line: standard library imports first, then third-party package imports, then local/custom module imports, following the widely-adopted PEP 8 style convention.
  • Use a requirements.txt or Similar File to Track Dependencies: For any real project using third-party packages, maintain a 'requirements.txt' file (or use a tool like Poetry) listing every required package and its specific version, allowing anyone else (or your future self) to recreate the exact same working environment using 'pip install -r requirements.txt'.
What is the difference between 'import module_name' and 'from module_name import specific_item' in Python?
'import module_name' imports the ENTIRE module as a single namespace object, requiring every subsequent reference to any of its contents to use the 'module_name.' prefix (e.g., 'math.sqrt(16)'). 'from module_name import specific_item' imports ONLY the specifically named function, class, or variable directly into the current namespace, allowing it to be used directly without any prefix at all (e.g., just 'sqrt(16)' after 'from math import sqrt'). The full-module import style is generally considered clearer for readability, since it's always obvious where a given function call originated from, while the from-import style offers more concise code when a specific item is used frequently throughout a file.
Why is using 'from module import *' generally discouraged in production Python code?
Using the wildcard import 'from module import *' brings every public name defined in that module directly into the current file's namespace without any prefix, which creates two significant problems: first, it becomes unclear when reading the code later exactly which module a given function or variable actually came from, making the code harder to understand and maintain; second, and more seriously, it risks silently overwriting existing variables, functions, or even other imported names that happen to share an identical name with something in the wildcard-imported module, potentially causing very confusing and hard-to-trace bugs. Explicit imports (either full-module or named from-imports) avoid both of these issues by keeping the source of every name clear and unambiguous.
What is a circular import in Python, and how would you typically resolve one?
A circular import occurs when two (or more) Python modules depend on each other, directly or indirectly — for example, module A contains 'import module_b' at its top, while module B simultaneously contains 'import module_a' at its top. When Python attempts to load either module first, it encounters the other module's import statement before that other module has finished being fully defined, often resulting in an 'ImportError: cannot import name ...' because the specific function or class being imported doesn't exist yet at that point in the loading process. This is typically resolved by restructuring the code: moving the genuinely shared logic that both modules need into a new, third module that both A and B can independently import from, or by moving the problematic import statement to be local within a specific function (deferring it until it's actually needed, by which point both modules have finished loading), rather than at the very top of the file.
Write a Python program that imports the 'datetime' module and prints the current date and time.
import datetime current_datetime = datetime.datetime.now() print("Current date and time:", current_datetime) # Example Output (actual date/time will vary): # Current date and time: 2026-07-23 14:32:10.123456
What is wrong with the following code, and what error will it produce when run? import pandas as pd data = pd.DataFrame({'col1': [1, 2, 3]}) print(data)
This code will only work correctly if the 'pandas' package has already been installed in the current Python environment using 'pip install pandas'. If pandas has NOT been installed, running this code will produce a 'ModuleNotFoundError: No module named 'pandas''. Unlike built-in standard library modules (like 'math' or 'datetime'), third-party packages like pandas are not included with a default Python installation and must be explicitly installed first using pip before they can be imported and used.
Create two Python files: 'greetings.py' containing a function 'say_hello(name)' that prints a greeting, and 'main.py' that imports and calls this function from greetings.py.
# greetings.py def say_hello(name): print("Hello, " + name + "! Welcome to CompileX.") # main.py from greetings import say_hello say_hello("Aditi") # Output (when running main.py): # Hello, Aditi! Welcome to CompileX. # This demonstrates a custom, user-defined module: 'greetings.py' is treated as an importable module simply because it's a .py file in the same directory, and 'main.py' imports its 'say_hello' function directly using the from...import syntax.

Modules in Python are reusable files of code that can be brought into a program using the 'import' statement, whether they're built-in standard library modules, installable third-party packages, or a developer's own custom files. Understanding the difference between full-module imports, specific from-imports, and aliasing with 'as', while avoiding pitfalls like wildcard imports and circular dependencies, is essential for organizing Python programs effectively and leveraging Python's vast ecosystem of reusable, pre-built functionality.

© 2026 CompileX. Maintained by Aditya Kumar Sharma.