Definition
At the start, a program fits in a single file. Then it grows, a useful function would deserve to serve elsewhere, and above all most of what you need has already been written by other people. Copying that code into every project would be absurd: as many copies as files, each to fix on its own.
import answers that exact annoyance. The statement fetches a module, meaning a Python file written elsewhere, and makes its content usable here. That module may come from the standard library shipped with Python, from an installed package, or from another file of your project: the same line serves all three cases.
# json ships with Python, there is nothing to install
import json
# The module name becomes the prefix of everything it holds
data = json.loads(response)That prefix is not a constraint, it is a landmark. Meeting json.loads in the middle of three hundred lines, you know where the function comes from without scrolling back to the top of the file or opening the documentation.
The three writings
The import line is written in three ways, and that choice decides what you handle in the whole rest of the file.
| Written | What it gives |
|---|---|
import json | The whole module, reached through json. |
from json import loads | A single function, reachable with no prefix |
import numpy as np | The whole module, under a shorter name |
The first keeps the origin visible everywhere, at the cost of a repeated prefix. The second brings in a single item and lightens the writing, but its origin now sits only on the import line. The third renames on the way through: np for numpy or pd for pandas are conventions so widespread that they read like the full name.
A fourth form exists, from module import *, which dumps everything into the current namespace. It is discouraged without exception: nothing shows where a name comes from any more, and two modules exposing the same name overwrite each other silently, with no error and no warning.
Where Python looks
When you write import json, Python guesses nothing: it walks a list of folders in a fixed order and stops at the first one carrying that name. The folder of the file being run comes first, then the environment paths, then the installed packages.
That order explains most ModuleNotFoundError messages, but also a family of otherwise baffling bugs. Since the current folder comes first, one of your own files named after a known library takes its place.
A json.py dropped into a project diverts every import json of that folder towards itself, standard library included. The errors that follow speak of a missing attribute, never of the file name, and the hunt can eat an hour.
The environment counts as much as the name. A package installed with pip only exists inside the virtual environment where the install happened. An import that worked yesterday and fails today often comes from there: the code did not change, the interpreter running the script did.
What happens on the first import
An import does not merely make a name available: Python opens the module file, runs it from top to bottom once only, then files the result in a cache.
The consequence often surprises: everything a module writes at its top level runs inside whoever imports it. A print left over from a test, a connection opened, a long data preparation, all fires at import time. This is why a file meant to be executed keeps its launch code behind an entry point, the well-known if __name__ == "__main__": line.
The cache, for its part, makes repeated imports free. A module imported in ten files is loaded once per run. Multiplying import lines therefore costs nothing in performance, only in readability the day they serve no purpose.
That cache is confusing in an interactive session: editing a module then running its import again changes nothing, Python sees it as already loaded and does not read the file again. The session has to be restarted, or importlib.reload used.
Where to put imports
PEP 8 asks for every import to be gathered at the top of the file, grouped in three blocks separated by a blank line: standard library, third-party packages, then project modules. The point is to see in five seconds what this file depends on, without reading it.
Two situations justify moving an import down into the code. Breaking a circular dependency, when two modules import each other and neither manages to finish loading. And deferring an expensive load, paid only when the branch concerned is taken.
Frequently asked questions
Why can Python not find my module?
The ModuleNotFoundError message gives the exact name it looked for, and the explanation is nearly always one of these three: the package is not installed in the active environment, the install name differs from the import name (pip install pillow gives import PIL), or your file sits in a folder Python does not explore.
Should the whole module be imported, or only what is used?
Importing the whole module keeps the origin in sight while reading, which is worth a lot in a long file. Importing one precise function lightens the writing when it shows up twenty times. Both are correct, and staying consistent within a file matters more than the choice.
Can an import slow down startup?
Yes, some computation libraries take close to a second to load, and that second is paid before the first useful line. On a command-line script that must answer fast, moving a heavy import inside the function that needs it becomes a legitimate optimisation.