Historical Context & Motivation
When programming languages were first developed, every programmer had to write everything from scratch. Need to calculate a square root? You'd have to code the algorithm yourself. Need to generate a random number? Same story. This approach was slow, error-prone, and incredibly tedious. As programs grew more complex, developers realized they needed a way to reuse code that others had already written and tested.
The idea of organizing code into reusable chunks — called modules — became a cornerstone of modern programming. Python, created by Guido van Rossum in the early 1990s, embraced this philosophy from the start. Python's motto of "batteries included" means it ships with a massive collection of pre-built modules called the Standard Library, ready for you to use in your own programs.
#include statements, making code reuse a standard practice.import statement, making code reuse elegant and intuitive.So how do you actually bring these powerful, pre-built tools into your own Python program? That's where the import and from statements come in. Understanding how to import modules is one of the most important skills you'll learn as a Python programmer — it's the gateway to Python's enormous power.
Core Principles & Definitions
Before you start importing, you need to understand a few fundamental concepts. A module is simply a Python file (ending in .py) that contains functions, classes, and variables you can use in other programs. A package is a collection of related modules organized in a folder. Together, modules and packages form the building blocks of Python's code reuse system.
Module
math.py contains mathematical functions like sqrt() and constants like pi.import Statement
math.sqrt(25).from ... import Statement
sqrt(25) without the module prefix.Namespace
Alias (as keyword)
as. For example, import numpy as np lets you type np instead of the full name.import math is like bringing the entire toolbox to your desk — you have everything but need to say "toolbox → hammer" (math.sqrt) each time. Using from math import sqrt is like taking just the hammer out of the toolbox and placing it directly on your desk — you can grab it immediately by name.How Importing Works — A Visual Guide
import statement.When you write import math, Python doesn't just magically know where the math module lives. It follows a specific search order: first checking if it's already loaded in memory (the cache), then searching through a list of directories stored in sys.path. This list starts with your current directory, then looks in the standard library, and finally checks installed third-party packages. If Python can't find the module anywhere, it raises a ModuleNotFoundError.
Import Syntax Deep Dive
Syntax Pattern 1: import module
The simplest form of importing loads the entire module and creates a namespace for it. You then access everything inside using dot notation. This is the safest approach because it keeps everything neatly organized under the module's name.
module_name.function() or module_name.variable. Example: import random → random.randint(1, 10)Syntax Pattern 2: from module import name
This pattern lets you cherry-pick specific items from a module. The imported names are placed directly into your program's namespace, so you can use them without a prefix. You can import multiple items by separating them with commas.
from math import sqrt, pi → sqrt(25) returns 5.0Syntax Pattern 3: import module as alias
When a module name is long or you want a shorter handle, you can assign it an alias using the as keyword. This is extremely common in the Python community — for instance, almost everyone writes import numpy as np. You can also alias specific imports: from math import factorial as fact.
import datetime as dt → dt.date.today()Syntax Pattern 4: from module import * (Use with Caution!)
The wildcard import from math import * dumps everything from a module into your namespace. While convenient, this is generally discouraged because it can overwrite your own variables without warning and makes it impossible to tell where a function came from when reading the code. Stick to specific imports whenever possible.
from module import * in your programs. If a module defines a function called open() and you use a wildcard import, it will silently replace Python's built-in open() function. This kind of name collision can cause bugs that are extremely hard to track down.Popular Python Modules & Their Uses
Python's Standard Library is enormous, and knowing which modules exist can save you hours of coding. Here are some of the most commonly used modules you'll encounter in high school computer science and beyond. These are all built into Python — no extra installation needed.
pip install. These third-party packages are what make Python so powerful for specialized tasks like data science and web development.| Module | Type | Example Import | Example Use |
|---|---|---|---|
math | Standard Library | from math import sqrt | sqrt(144) → 12.0 |
random | Standard Library | import random | random.randint(1, 6) → dice roll |
datetime | Standard Library | from datetime import date | date.today() → current date |
os | Standard Library | import os | os.getcwd() → current folder |
turtle | Standard Library | import turtle | turtle.forward(100) → draw line |
Worked Example — Building a Dice Roller
Let's walk through a complete example that demonstrates multiple import techniques. We'll build a dice-rolling program that also calculates the statistical average of all rolls. This example uses two different modules and shows both import and from...import styles.
import random for the full module and from statistics import mean for just the specific function we want.import random
from statistics import meanrandom.randint(1, 6) to simulate rolling one die. Notice we use dot notation because we imported the full random module.def roll_dice():
return random.randint(1, 6)roll_dice() generates a new random number between 1 and 6.rolls = [roll_dice() for _ in range(10)]
Example output: [3, 5, 1, 6, 2, 4, 4, 1, 5, 3]mean directly from the statistics module, we can call it without any prefix. This shows the benefit of selective importing — cleaner, shorter code for frequently used functions.average = mean(rolls)
print(f"Rolls: {rolls}")
print(f"Average: {average}")
Output: Average: 3.4import random
from statistics import mean
def roll_dice():
return random.randint(1, 6)
rolls = [roll_dice() for _ in range(10)]
average = mean(rolls)
print(f"Rolls: {rolls}")
print(f"Average: {average}")Comparing Import Styles — When to Use Which
Each import style has its own strengths and trade-offs. Choosing the right one depends on how many items you need from a module, how often you'll use them, and whether readability or brevity matters more in your particular program. Here's a comprehensive comparison to help you decide.
| Import Style | Strengths | Weaknesses |
|---|---|---|
import math | Clear origin of every function. No naming conflicts. Easy to discover available functions with autocomplete. | Verbose — you must type math. every time. Can feel repetitive in math-heavy code. |
from math import sqrt | Clean, concise code. Only loads what you need. Great when you use a function many times. | Risk of name collisions if two modules have the same function name. Harder to trace where a function came from. |
import math as m | Shorter prefix saves typing. Keeps namespace safety. Community conventions (e.g., np, pd) make code universally readable. | Custom aliases can confuse readers if they're non-standard. Still requires the prefix. |
from math import * | Maximum convenience — all names available directly. Quick for interactive exploration in the REPL. | Pollutes namespace. Can silently overwrite existing names. Makes code hard to debug and understand. |
import math is like ordering the full menu — you have everything, but you need to specify the section each time ("appetizers → nachos"). from math import sqrt is like ordering just the nachos directly — quick and easy, but if two restaurants both have "nachos" on the menu, things get confusing. import math as m is like giving the restaurant a nickname — you still point to the menu, but it's faster to say.Connection to Advanced Concepts
The import system you've learned here is the foundation for much more advanced Python programming. As your projects grow larger, you'll move beyond importing from the Standard Library to creating and importing your own modules, and eventually organizing code into multi-file packages.
| What You Know Now | What Comes Next |
|---|---|
Importing built-in modules like math and random | Creating your own modules — any .py file can be imported by another file in the same directory |
| Flat imports from a single module | Packages with __init__.py files that organize modules into nested folders, like from mypackage.utils import helper |
Using pip install for third-party packages | Virtual environments (venv) to isolate project dependencies and requirements.txt files to share them |
Simple import at the top of files | Conditional imports, lazy imports, and the importlib module for dynamic loading at runtime |
| Running scripts directly | The if __name__ == "__main__": guard that controls whether code runs on import or only when executed directly |
One of the most important advanced patterns is the main guard. When you create a file called helpers.py with some functions, another file can import helpers to use those functions. But if helpers.py also has code that runs when the file is executed (like test prints), that code will run during import too — unless you wrap it in if __name__ == "__main__":. This guard is something you'll encounter constantly in real-world Python projects.
Practice Problems
import math and from math import sqrt? Explain what each does and how you would call the square root function after each import.math module and uses it to calculate the area of a circle with radius 7. Use math.pi for π and display the result rounded to 2 decimal places.NameError. Find the bug and fix it:
import random
number = randint(1, 100)
print(f"Your random number is: {number}")random and string modules. Hint: string.ascii_letters contains all letters, and string.digits contains '0123456789'.graphics.py and audio.py. Both define a function called play(). If you write from graphics import play followed by from audio import play, what happens? Which play() function will run when you call play()? Propose a better import strategy that avoids this problem.Lesson Summary
Python's import system lets you bring pre-built code into your programs, saving you from reinventing the wheel. A module is any Python file containing reusable code. You can load an entire module with import module_name and access its contents using dot notation, or selectively import specific items with from module import name for shorter, cleaner code. The as keyword creates aliases that shorten long module names while preserving namespace safety.
Python's Standard Library provides hundreds of built-in modules like math, random, and datetime — no installation needed. Beyond the standard library, the Python Package Index (PyPI) offers over 400,000 third-party packages. Always place imports at the top of your file, avoid wildcard imports (from module import *) to prevent name collisions, and follow PEP 8 conventions for clean, professional code.