Topic 6.1
import: Using Code from Other Files
In one line
import loads another module and gives you a name for it. Python runs the module's file once, the first time it is imported, and keeps the result in sys.modules so that every later import is instant and shares the same module object.
Think of it like this
A school library. You do not copy every book into your bag. You ask the librarian for one by name. The first time anyone asks for a rare book, the librarian walks to the storage room, fetches it and puts it on the front shelf. After that, everyone who asks gets the same copy from the shelf, instantly. import is asking the librarian; sys.modules is the front shelf.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Module
- A Python file (or a built-in) that other code can import. Its top-level names become its attributes.
- import
- The statement that finds a module, runs it the first time, and gives you a name for it.
- Module object
- The Python object that represents a loaded module. You use it with a dot:
math.pi. - Namespace
- A set of names and what they refer to. Every module has its own, so two modules can both have a
totalwithout clashing. - sys.modules
- A dictionary of every module already loaded, keyed by name. It is Python's cache of finished imports.
- Alias
- A different name you choose with
as, for exampleimport numpy as np. - Standard library
- The modules that come with Python itself, such as
math,jsonandpathlib. - Third-party package
- Code written by other people, installed from PyPI, such as
numpyorpydantic. - Star import
from module import *. It copies every public name and is usually a bad idea.- Cache
- A place where a finished result is kept, so the work is not done twice.
sys.modulesis the cache of imports. - Bytecode
- The compact instructions Python turns your file into before it runs it (Topic 0.8).
Step by step
01Three ways to import the same thing
All three lines below load the math module. They only change which names you get. Notice that math and m are the same object under two names, just like two labels on one box.
from math import pi as PI combines both tricks: it copies one name out and renames it.
import math
import math as m
from math import sqrt, pi as PI
print(math.sqrt(16), m.sqrt(25), sqrt(36))
print(math is m)02What happens on the first import
When Python meets import greeter, it does these steps in order. (1) Look in sys.modules for the key "greeter". If it is there, use it and stop. (2) Otherwise, search for a file or package called greeter (Topic 6.2 shows where). (3) Create an empty module object. (4) Store it in sys.modules. (5) Run the file's code inside that object, top to bottom. (6) Bind the name greeter in your file.
Step 5 is the surprising one: importing runs code. If the file has a print at the top level, it prints, once. Step 4 comes first on purpose: while the file runs, the half-built module is already in sys.modules, which is what makes circular imports (Topic 6.3) behave the way they do. If step 5 raises an error, Python removes the entry again, so the next import tries afresh.
03The cache: a second import does nothing
Because of step 1, importing the same module again is nearly free and never reruns the file. Both names below point at the single object stored in sys.modules["greeter"]. That is also how a module acts as a shared singleton (one object that everybody uses): a settings module imported by ten files is one object, not ten.
If you edit a module while a program is running, the new code is not picked up. importlib.reload(module) forces a rerun. It is useful in a notebook or the REPL, and rarely in real programs.
import sys
import greeter # runs greeter.py now
import greeter as again # does not run it again
print(sys.modules["greeter"] is greeter is again)04from-import copies a reference
from settings import limit makes a new name limit in your file that points at the same value settings.limit pointed at at that moment. If code later does settings.limit = 99, your limit still shows the old value, because you hold your own label on the old object.
When a value can change while the program runs, import the module and read settings.limit each time. When a name is a stable function or constant, from is fine and reads well.
import settings
from settings import limit # a snapshot of the value now
settings.limit = 99
print(settings.limit) # 99: looks up the name in the module each time
print(limit) # still the old value05Why import * is a trap
A star import copies every public name (every name without a leading underscore, unless the module defines __all__, Topic 6.3). If the module defines something called sum, max or list, it replaces yours, with no warning.
Nothing is wrong with Python here. It does what you asked. The problem is that a reader of your file cannot tell where helper() came from, and an upgrade of the other module can add a new name that quietly breaks yours.
from mathy import * # mathy.py defines its own sum()
print(sum([1, 2, 3])) # not the built-in any more06Two different import errors
ModuleNotFoundError means Python could not find a module of that name at all (a typo, or a package that is not installed). ImportError: cannot import name means the module exists but does not contain that name. Python 3.12 and later often adds a helpful "Did you mean".
ModuleNotFoundError is a more specific kind of ImportError (Topic 7.1 explains how errors are grouped into families), so except ImportError catches both. Library code uses this to offer optional features: try to import a fast package, and fall back to a slower built-in one.
In the snippet, try: means "attempt this", and except ImportError: means "if that failed with an import error, do this instead". Topic 7.1 teaches try fully.
try:
import orjson as json_lib # a fast third-party JSON library
except ImportError:
import json as json_lib # the standard library always works
print(json_lib.__name__)07Where imports go, and how they are grouped
PEP 8 (Topic 1.11) asks for imports at the top, in three blocks: the standard library, then third-party packages, then your own project's modules, with a blank line between blocks and each block sorted alphabetically.
The grouping tells a reader at a glance what your file needs from the world. A long list from the third block means your file does too much; a long list of standard-library imports in a tiny file is usually fine.
import json
import sys
from pathlib import Path
import httpx
from pydantic import BaseModel
from myapp.config import load_settings
from myapp.storage import saveTry it yourself
- 1
Predict the reload
In the second example, comment out the
importlib.reload(greeter)line and run it. How many times doesgreeter.py is running nowappear? Predict, then check. Put the line back and the count changes to two. - 2
Import it two ways
In the first example, add
from math import sqrt as rootand printroot(81). Then printmath.sqrt is root. It printsTrue: a from-import copies the reference to the very same function. - 3
Catch the star-import
In the third example, change
from mathy import *tofrom mathy import helper, run it, and watchsumstay the built-in. This is the fix: import only the names you mean to use.
Code & diagrams
`math` and `m` print the same result because they are the same module object. Try `print(sqrt)` to see that a from-import gives you the function itself.
Expected output
4.0 5.0 6.0
pi is about 3.1416
same module object: True
math in sys.modules: True
type of sys.modules: dict
module mathThe example writes `greeter.py` first, so you can watch the import happen. `Path(...).write_text(...)` puts text into a file (Topic 7.6 teaches `pathlib`), and `\n` inside the text is a new line. `importlib.invalidate_caches()` tells Python to re-read the folder, which is needed when a file was created a moment ago. `importlib.reload` forces a second run.
Expected output
before import: False
greeter.py is running now
after first import: True
after more imports, still one run
True hello
greeter.py is running now
reload runs the file againThe file `mathy.py` is written first (`def` makes a function, Phase 5). It defines its own `sum`. After `from mathy import *`, the name `sum` in this file points to it. The real built-in is still reachable through the `builtins` module.
Expected output
before: 6
after star import: my own sum
builtin still fine: 6This is why modules exist. The tax rule lives in one file, `billing.py`. Every other file that needs a bill imports it, so the rate is changed in one place only. Here the file is written first so the example is self-contained.
Expected output
subtotal: 255.49
with GST: 301.48
rate used: 0.18Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Misspell a module name
Write import mathh instead of import math.
import mathh
print(mathh.sqrt(16))Break #2
Import a name that is not there
Write from math import sqroot.
from math import sqrootMyth vs fact
Myth
import just copies the file's text into mine, like a paste.
Fact
It runs the file once in its own namespace and gives you the resulting module object. Its names live in the module, which is why you write math.sqrt and not just sqrt.
Myth
Importing a module twice runs it twice.
Fact
The second import finds the module in sys.modules and does nothing. Only importlib.reload runs it again.
Myth
from x import name is faster or lighter than import x.
Fact
Both load the entire module. The difference is only which names appear in your file.
Myth
Star imports are fine when the module is small.
Fact
Small modules grow. The risk is silent replacement of your names, and a reader who cannot tell where a name comes from.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Import time is real. A module with heavy top-level work (loading a model, opening a connection, importing a huge library) slows every program that imports it, including quick command-line tools and tests. Keep import-time work small: define things, do not run them. Python 3.15 adds explicit lazy imports for exactly this problem.
- ▸
python -X importtime main.pyprints how long each import took, in microseconds, to stderr. It is the first tool to reach for when a program starts slowly, and it often shows that one import of an AI library takes most of the startup. - ▸
sys.modulesis a plain dictionary, and the import system is built on documented hooks (finders onsys.meta_path, loaders inimportlib). Tests sometimes patchsys.modulesto replace a heavy dependency, and plugin systems load modules by name at run time withimportlib.import_module("pkg.name"). Two traps follow from the cache:importlib.reloadre-runs the file in the same module object, so objects created earlier (classes, instances) are not replaced andisinstancechecks can behave oddly; and a name imported withfrom m import xkeeps the old object after a reload. - ▸
Because modules are cached singletons, module-level state is shared by every importer. That is convenient for configuration and dangerous for anything mutable and per-request. Prefer passing objects into functions over keeping them in module globals.
Remember this
- 1
A module is just a Python file.
math.py,random.pyand your ownhelpers.pyare all modules. The statementimport mathmeans: find the module calledmath, run it if it has not been run yet, and bind the namemathin my file to the finished module object. After that,math.sqrtmeans "the namesqrtinside that module". - 2
There are three common forms.
import mathgives you the module under its own name.import math as mgives it a shorter name (import numpy as npis a famous example).from math import sqrt, picopies names out of the module into yours, so you writesqrt(16)with nomath.prefix. All three load the whole module; they differ only in which names appear in your file. - 3
The first import of a module does real work: Python searches for the file, reads it, turns it into bytecode (the compact instructions Python really runs, Topic 0.8), and runs it from top to bottom. This is when its
defandprintlines happen. Python stores the module object in the dictionarysys.modules, keyed by name, just before it runs the file (so the entry already exists while the file runs, and is removed again if the file fails with an error). The secondimportof the same name finds the key and does nothing more. This is why a module's top-levelprintruns only once, and why two files that importconfigshare the sameconfigobject. - 4
import *(as infrom math import *) dumps every public name of a module into yours. It is discouraged because you can no longer tell where a name came from, and a name from the module can silently replace one of yours, or even a built-in such assum. Preferimport moduleor an explicitfrom module import name. - 5
from m import namemakes your own label for whatevernamepointed at at that moment (Topic 1.1: a name is a label stuck on a value). If the module later points itsnameat a new value, your label does not follow. Useimport mand writem.namewhen you need the live value. - 6
The two forms also fail differently. A missing module gives a
ModuleNotFoundError. A missing name inside a module that does exist gives anImportError. - 7
Imports usually go at the top of the file, grouped in three blocks separated by a blank line: standard library first, third-party packages second, your own modules third. Tools such as Ruff (Topic 11.6) sort and check this for you. A file whose imports you can read in five seconds is a file whose dependencies you understand.
Explain it without notes
What exactly happens when Python executes import greeter for the first time?
What is sys.modules, and what does it explain?
What is the difference between import x and from x import name?
Why is from module import * discouraged?
Practice
Import sqrt from math under the name root, then print root(144).
Write a program that imports json, then prints "json" in sys.modules (import sys too) and the module's name json.__name__.
Write a file shapes.py that sets SIDES = 4 and prints loading shapes. (Create it from your program with Path("shapes.py").write_text(...), as in the second example.) Import it twice and show that the message appears only once.
Import sys. Print "antigravity" in sys.modules. Then import the module colorsys (do not import antigravity) and print the same check again. Explain the two answers.
Trade-offs
- ↔
**
import xvsfrom x import name.** The first keeps names qualified and always reads the live value, at the cost of typing the prefix. The second is shorter but takes a snapshot and hides the origin. Useimport xfor big modules andfromfor a few clearly named helpers. - ↔
Importing at the top vs inside a function. Top-level imports make dependencies visible and fail early. An import inside a function delays a slow or optional import until it is needed, or breaks a circular import (Topic 6.3), but it hides dependencies and repeats a (cheap) cache lookup on every call.
Done when you can
Done when you can explain the six steps of a first import, including that the file is executed.
Done when you can show that a second import does not rerun a module, using
sys.modules.Done when you can write
import x,import x as yandfrom x import a as b, and say which names each creates.Done when you can explain why
from x import *is discouraged, with thesumexample.Done when you can tell a
ModuleNotFoundErrorfrom anImportError: cannot import name.