Why this matters
Every useful Python program leans on code written by someone else: the standard library, third-party packages, and other files of your own project. The import statement is the doorway to all of it. Knowing exactly what import does (where it looks, what it creates, and that it runs a file only once) explains many puzzling situations: why your file named random.py broke import random, why a change to a module does not show up in a running REPL, and why from x import y sometimes seems to "not update".
A module is a file, and a module object
A module is simply a .py file. If you save this as greet.py:
"""Greeting helpers."""
DEFAULT_NAME = "world"
def hello(name=DEFAULT_NAME):
return f"Hello, {name}!"
print("greet.py is running")
then in another file in the same directory you can write:
import greet
print(greet.hello())
print(greet.hello("Ada"))
print(greet.DEFAULT_NAME)
greet.py is running
Hello, world!
Hello, Ada!
world
What did import greet actually do? Three things:
- Find a module called
greet(more on where it looks below). - Create a module object and execute the file's code from top to bottom inside that module object's namespace. Every top-level
def, assignment andimportingreet.pycreates a name in that namespace. That is whygreet.py is runningwas printed: top-level code runs at import time. - Bind the name
greetin the importing file to the module object.
A module object is an ordinary object, and its namespace is (underneath) a dictionary:
import greet
print(type(greet))
print(greet.__name__)
print(sorted(k for k in vars(greet) if not k.startswith("__")))
greet.py is running
<class 'module'>
greet
['DEFAULT_NAME', 'hello']
vars(greet) returns the module's __dict__, the dictionary that maps names to objects. greet.hello is just a lookup of the key "hello" in that dictionary. Seen this way, a module is a namespace: a container that keeps its names separate from yours, so greet.hello and some other module's hello never collide.
The module cache: sys.modules
Importing is expensive (find the file, compile it to bytecode, run it), so Python does it once per process. After the first import, the module object is stored in the dictionary sys.modules, keyed by name. Every later import greet anywhere in the program checks sys.modules first and, finding it, just binds the name to the existing object.
import greet
|
v
"greet" in sys.modules? --yes--> bind name to that object (no code runs)
|
no
v
search sys.path for greet --> create module --> run greet.py in it
--> store in sys.modules["greet"] --> bind name
Consequences:
- Module-level code runs once, however many files import the module. That makes modules a natural place for shared configuration and for things that should be set up once.
- In the REPL, editing
greet.pyand typingimport greetagain does nothing, because the cached module is reused. Restart the REPL (or useimportlib.reload(greet), which has its own caveats). - All importers share one module object. If one file changes
greet.DEFAULT_NAME, every other file that readsgreet.DEFAULT_NAMEsees the change.
Some modules are already in sys.modules before your first line runs, because the interpreter itself needed them during startup (sys, os, builtins and a few others). Most of the standard library is loaded only on demand.
Where Python looks: sys.path
To find a module that is not cached, Python searches a list of directories, sys.path, in order, and takes the first match:
import sys
for p in sys.path:
print(p)
/home/ada/project
/usr/lib/python313.zip
/usr/lib/python3.13
/usr/lib/python3.13/lib-dynload
/usr/local/lib/python3.13/dist-packages
/usr/lib/python3/dist-packages
- The first entry is the directory of the script you ran (
python3 main.pyputs main.py's directory there). In the REPL it is the current directory. This is howimport greetfinds yourgreet.py. - Then the standard library directories.
- Then
site-packages(ordist-packageson Debian): where installed third-party packages live.
A few modules (like sys, math and builtins) are compiled directly into the interpreter and are found before the path is searched at all; sys.builtin_module_names lists them.
Shadowing: the classic trap
Because your script's directory comes first, a file of yours with the same name as a standard module hides it:
project/
random.py <- you wrote this to experiment with random numbers
main.py <- contains: import random; print(random.randint(1, 6))
Traceback (most recent call last):
File "/home/ada/project/main.py", line 2, in <module>
print(random.randint(1, 6))
^^^^^^^^^^^^^^
AttributeError: module 'random' has no attribute 'randint' (consider renaming '/home/ada/project/random.py' since it has the same name as the standard library module named 'random' and prevents importing that standard library module)
Python 3.13 now spells out the cause, but older versions just say has no attribute. Never name your files after modules you use: random.py, json.py, email.py, test.py, string.py, csv.py. Also delete the stale __pycache__/random.cpython-313.pyc after renaming.
The forms of import
import math # name math -> module; use math.sqrt(2)
import collections.abc # submodule; use collections.abc.Mapping
import numpy as np # bind the module under a different name
from math import sqrt, pi # bind sqrt and pi directly in your namespace
from math import sqrt as root # under a different name
from math import * # bind every public name (avoid this)
import math binds one name, math, to the module. You then write math.sqrt, which keeps it obvious where sqrt comes from.
from math import sqrt still imports (and caches) the whole math module; it then looks up sqrt in it and binds your name sqrt to that same function object. There is no "partial import" that saves loading time.
That last point has a subtle consequence that the third exercise explores. After from counter import count, your count is a separate name that refers to whatever object counter.count referred to at that moment. If the counter module later rebinds its own count, your name does not follow; names are labels, and you only copied the label's target, not a link to the other label. Reading through the module, counter.count, always gets the current value.
from module import * dumps every public name into your namespace. It makes it impossible to tell where a name came from and can silently overwrite your own names. Use it only in the REPL, if at all.
Style conventions (PEP 8): imports go at the top of the file, one module per line, grouped as standard library, then third-party, then your own modules, with a blank line between groups.
Common mistakes
ModuleNotFoundError: No module named 'greet': the file is not in anysys.pathdirectory. Usually you ran Python from a different directory than you think, or the script is in a subdirectory. Printsys.pathto check.- Shadowing a standard module with your own file name, as above. The symptom is an
AttributeErrororImportErrorabout a function that definitely exists. ImportError: cannot import name 'helo' from 'greet': the module was found but has no such name; check spelling, or whether the name is defined only inside a function.- Expecting a re-import to pick up edits in the REPL. The cache wins.
- Circular imports:
a.pyimportsb, andb.pyimportsa. Whenbruns,ais only half-executed, so names defined later inado not exist yet, givingImportError: cannot import name 'x' from partially initialized module 'a' (most likely due to a circular import). Restructure so shared code lives in a third module.
Recap
- A module is a
.pyfile; importing it creates a module object, runs the file once in that object's namespace, and binds a name to it. - Imported modules are cached in
sys.modules; later imports reuse the same object without running code. - Modules are found by searching
sys.path, whose first entry is the running script's directory, so your files can shadow standard ones. import mbinds the module;from m import xbinds your own name to the objectm.xreferred to at import time.
Going further: run python3 -v -c "import json" 2>&1 | grep json to watch the import system search and load files, and read the "The import system" chapter of the Python Language Reference.