Each Python file is a module you can
import into another Python file. If the file is in
the same directory, you can use an unqualified
import statement:
# module.py
def useful_function():
return "I'm being useful!"# use_module.py
import module
print("'module' imported")
if __name__ == "__main__":
print(module.useful_function())
#: 'module' imported
#: I'm being useful!Importing a module makes its namespace reachable in
the importing file, under the module’s name. Reaching those
names through the module’s name keeps them from clashing with
the local ones. To call useful_function(), you must
qualify it with the name of the module:
module.useful_function().
The code at the end of use_module.py starts with
an if clause that checks whether the standard
variable __name__ equals the string
"__main__". In Python, any identifier that begins
and ends with double underscores (commonly called a “dunder”) is
special in some way. Dunder methods, for example, connect your
class to the language’s operators and built-in functions; Classes shows how
print() and str() reach a class’s
__str__().
The if exists because you can also use any file
as a library module within another program. In that case, you
want its definitions and none of the code at the bottom of the
file. The condition is true only when you run this file
directly. That is, __name__ is
"__main__" when you use the command line:
python use_module.py
However, if another program imports use_module.py as a module,
__name__ is "use_module" instead, so
the "__main__" block does not run. The next program
only imports use_module:
# import_module.py
import use_module
#: 'module' importedImporting use_module runs its top-level code,
including the print(), but not its
"__main__" block. Python runs that code once,
however many times the program imports the module:
# import_once.py
import sys
import use_module
import use_module as second
#: 'module' imported
print(use_module is second)
#: True
print(sys.modules["use_module"] is use_module)
#: Trueuse_module’s body runs once even though two
import statements name it. The first
import stores the finished module object in
sys.modules, a dict keyed by dotted module name.
Every later import of that name, from any file in
the program, finds it there and binds the same object instead of
re-running the file, so use_module and
second are one object. One consequence: editing a
module’s file changes nothing in a program that has already
imported it. Restart the program, or call
importlib.reload(use_module), which re-runs the
body into the existing module object. Reloading leaves every
name bound by a from ... import pointing at the old
objects, so restarting is the reliable choice.
To bring a name into the current namespace, use the
from keyword:
# using_from.py
from module import useful_function
if __name__ == "__main__":
print(useful_function())
#: I'm being useful!from copies the name’s current value into this
file rather than linking to it. Rebinding the name in the module
afterward does not reach the copy:
# app_settings.py
debug: bool = False
def show() -> None:
print(f"app_settings.debug is {debug}")# from_snapshot.py
import app_settings
from app_settings import debug
app_settings.debug = True
print(debug, app_settings.debug)
#: False True
app_settings.show()
#: app_settings.debug is Trueapp_settings.debug is now True, and
show() sees the new value because it looks the name
up in its own module every time it runs. The local
debug is a separate binding that from
set once, at import, so it still holds False.
Import the module and write app_settings.debug when
the value can change. Use from ... import for names
that keep the same value, such as functions and classes.
You can rename a module’s namespace during an import with the
as keyword:
# using_as.py
import module as m
if __name__ == "__main__":
print(m.useful_function())
#: I'm being useful!A module’s namespace is an ordinary dict you can read and
write. globals() returns it as a mutable
dict, the same dict Python already searches when it
looks up a top-level name. A dotted name reads that same dict:
module.__dict__ from outside is the same object
globals() returns inside module, so
module.useful_function() and a top-level lookup
inside module.py find the same
function. Assigning into that dict works like writing the
assignment directly:
# globals_demo.py
x = 10
print(globals()["x"])
#: 10
globals()["y"] = 42
print(y) # type: ignore # noqa: F821
#: 42The two directives on the print(y) line silence
the type checker and the linter, neither of which can see a name
that appears only as a dict key. That is the cost of creating
names this way, and a reason to keep it rare. Assigning into
globals() matters whenever code needs to define a
module-level name known only at runtime, such as a class built
dynamically under a computed name. Metaprogramming
builds classes that way, though it collects them in a dictionary
instead of in the module’s namespace.
As your programs get larger, you further organize your code into packages. A package is a directory that contains multiple modules, and it forms its own namespace with the name of that directory.
To make a directory a package, you put a special file named
__init__.py in it. __init__.py runs
once, before any module inside the package loads. An empty
__init__.py, the common case, only flags the
directory as a package.1 One with content
usually re-exports the package’s public names: if a_package/__init__.py
re-exported function1,
from a_package import function1 would work and
callers would never learn which submodule defines
function1. You can still import a directory without
__init__.py as a namespace package, but an
explicit __init__.py makes the package’s identity
and boundary clear, so this book uses one by default.
To explore this, create a directory called
a_package whose __init__.py announces
itself, so the markers below show when it runs:
# a_package/__init__.py
print("initializing a_package")Now add two modules to the package:
# a_package/module1.py
print("importing module1 in a_package")
def function1():
return "function1 in module1 in a_package"# a_package/module2.py
print("importing module2 in a_package")
def function2():
return "function2 in module2 in a_package"To import a module from a package, you must qualify it with the package name:
# using_packages.py
import a_package.module1
import a_package.module2
#: initializing a_package
#: importing module1 in a_package
#: importing module2 in a_package
print("a_package" in dir(), "module1" in dir())
#: True False
print(a_package.module1.function1())
#: function1 in module1 in a_package
print(a_package.module2.function2())
#: function2 in module2 in a_packageimport a_package.module1 binds only the name
a_package in this file. Loading
module1 also stores that submodule as an attribute
of a_package, so
a_package.module1.function1() resolves. The shorter
module1.function1() fails here, since nothing binds
module1 in this file; it works in from_packages.py, where
from binds module1 directly.
Importing the package alone does not import what is inside it:
# package_only.py
import a_package
#: initializing a_package
print(hasattr(a_package, "module1"))
#: FalseNo submodule loads, and no message prints beyond the
package’s own. a_package.module1.function1() here
would raise
AttributeError: module 'a_package' has no attribute 'module1'.
The submodule becomes an attribute of the package only when
something imports it by name.
You can also name the package with from:
# from_packages.py
from a_package import module1, module2
#: initializing a_package
#: importing module1 in a_package
#: importing module2 in a_package
print(module1.function1())
#: function1 in module1 in a_package
print(module2.function2())
#: function2 in module2 in a_packageHere you no longer need to qualify the module with the package name.
You can bring specific functions into the namespace by naming both the package and the module:
# no_qualification.py
from a_package.module1 import function1
from a_package.module2 import function2
#: initializing a_package
#: importing module1 in a_package
#: importing module2 in a_package
print(function1())
#: function1 in module1 in a_package
print(function2())
#: function2 in module2 in a_packageno_qualification.py, from_packages.py, and using_packages.py print
the same loading messages, because from loads
exactly what import loads. The whole module runs
either way. The statement decides only which names this file
binds.
You can put a second package underneath the first one:
# a_package/b_package/__init__.py
print("initializing b_package")# a_package/b_package/module3.py
print("importing module3 in b_package")
def function3():
return "function3 in module3 in b_package"To import module3 you must specify both
packages:
# two_levels.py
from a_package.b_package import module3
#: initializing a_package
#: initializing b_package
#: importing module3 in b_package
print(module3.function3())
#: function3 in module3 in b_packageThe listings so far import a package’s modules from outside the package. A package’s own modules also import each other, and there the leading dot of a relative import means “the package containing this module”:
# a_package/module4.py
from .module1 import function1
def function4():
return f"function4 calls {function1()}"# use_module4.py
from a_package.module4 import function4
#: initializing a_package
#: importing module1 in a_package
print(function4())
#: function4 calls function1 in module1 in a_packagemodule4 finds its sibling without naming
a_package, so renaming the package breaks nothing
inside it. Two dots (..module1) reach the parent
package, so b_package/module3.py could import from
a_package that way. The absolute form
from a_package.module1 import function1 works from
inside the package too, and is the better choice when the import
crosses a package boundary. Keep relative imports for a
package’s own submodules.
A relative import needs a package context, and a file run as
a script has none. Running
python a_package/module4.py raises
ImportError: attempted relative import with no known parent package.
Run it as python -m a_package.module4 instead.
Two modules in a package can end up importing each other.
Python places the first one to load in sys.modules
before its body finishes, so a from import in the
second finds a partially initialized module and fails with
ImportError: cannot import name ... (most likely due to a circular import).
A plain import of that same module succeeds at this
point, since it only needs the module to exist in
sys.modules, not to have finished running. That
wording appears when the module’s file comes from anywhere but
the directory of the script you ran. When the file sits in that
directory (sys.path[0]), the usual case for two
modules beside your script, Python suspects a name collision
with a library instead of a cycle:
ImportError: cannot import name 'f' from 'modx' (consider renaming '.../modx.py' if it has the same name as a library you intended to import).
Python names the offending file by its full path, abbreviated
here as .... With a plain import, the
failure surfaces later, wherever the code first uses a name the
module has not defined yet. A cycle is a design signal: move the
shared piece into a third module both can import. When the cycle
exists only in annotations, an if TYPE_CHECKING:
import breaks it, because Python does not evaluate annotations
at import time (see Simulation).
Every name at a module’s top level is importable, because
Python has no private keyword. A module states its
boundary by convention and by one optional list.
A leading underscore marks a name as internal. It is a signal
to a reader rather than a barrier.
accounting._Engine still resolves, and
from accounting import _Engine still works. The
underscore changes one mechanical thing:
from accounting import * skips every name that
begins with one.
__all__ states the export list explicitly. You
assign it at module level as a list of strings naming the public
names, and from module import * then imports those
names and no others. A name listed in __all__
arrives even when it begins with an underscore. An
__all__ also gathers the intended surface into one
readable place, and documentation tools read it to report what a
module offers.
# exporting.py
__all__ = ["public", "helper"]
def public():
return "public"
def helper():
return "helper"
def _internal():
return "internal"
def undeclared():
return "undeclared"# star_import.py
from exporting import * # noqa: F403
print(sorted(n for n in dir() if not n.startswith("__")))
#: ['helper', 'public']Without __all__, the star import would bind
public, helper, and
undeclared: everything not underscored. With it,
only the listed names arrive, and the star import skips
_internal either way.
Neither the underscore nor __all__ stops
module._name. Both say which names a caller should
use, and that agreement lets a module serve as a façade over its
internals (Changing the
Interface) or as a shared single instance (Singleton).
To be the target of an import statement, a
module’s file name must be a valid Python identifier and not a
keyword. import my-mod is a syntax error.
(importlib.import_module("my-mod") still loads the
file, and plugin loaders reach oddly named files that way, but
choose an importable name anyway.)
Modules (.py files): short,
all-lowercase, with underscores between words if that improves
readability, the style called snake_case.
result.py,
cache_singleton.py, list_comprehension.pyResult.py (CapWords is for classes),
cacheSingleton.py (camelCase),
cache-singleton.py (hyphens aren’t importable)Packages (directories with
__init__.py): also short and all-lowercase, but
convention discourages underscores. Prefer a single run-together
word when you can.
mypackage, and underscores only when they
genuinely help (a_package)Tests follow pytest’s discovery convention:
test_*.py (or *_test.py). This book
uses test_*.py, e.g. test_result.py.
Don’t shadow standard-library modules. A file named
random.py, string.py, or
weakref.py can hide the stdlib one and break
imports, because Python searches the script’s directory before
the standard library (see PYTHONPATH below). Give a
shared module a distinctive name for the same reason: the first
config.py imported
anywhere in the process is the one every later
import config gets.
Windows and macOS default to case-insensitive filesystems, so
module.py and
Module.py name the same file there. Python’s import
system still matches names case-sensitively by default, so
import module fails to find a file saved as
Module.py, even on a filesystem that treats the two
names as one. PYTHONCASEOK turns that case check
off, so import module finds Module.py
again, bringing back the confusion the check exists to prevent.
Keep a module’s file name and its import spelled with the same
case, rather than relying on either behavior.
PYTHONPATHPython searches sys.path, a list of directories
it builds at startup. print(sys.path) shows it. Its
first entry is the directory of the script you ran (the current
directory when you use -m or the REPL), and that
entry is why use_module.py can
import module with no setup. The entries from
PYTHONPATH come next, and installed packages sit
further down. Running with -P drops that first
entry, so a local random.py can no longer shadow
the standard library.
What if your module or package isn’t in the same directory as
the Python file that imports it? The original solution was the
PYTHONPATH environment variable, which tells Python
where to look for modules and packages. PYTHONPATH
takes multiple paths. Python searches them in order and uses the
first one that holds your module or package, and raises a
ModuleNotFoundError when none of them does.
PYTHONPATH still works, but today you install
your package into the environment you use, and the install puts
it on the search path with no environment variable. Concretely,
with uv (this book’s tool of choice), that means
uv sync, or uv pip install -e . for an
editable install. The package resolves by name from anywhere,
and edits to its source take effect immediately, without
reinstalling.
Every import so far runs the target module’s
top-level code immediately, and that is why importing
a_package.module1 printed its message as it loaded.
For a large program that imports many modules but uses only some
of them on any given run, that eager work slows startup.
Python 3.15 (PEP
810) adds the lazy soft keyword: a keyword only
inside an import statement, the way
match is a keyword only inside a match
statement (Control
Flow). A lazy import defers loading the module
until the first time you use the imported name, so a run pays
only for the modules it uses, while all imports stay at the top
of the file:
# lazy_imports.py
lazy import json
lazy from pathlib import Path
print(json.dumps({"a": 1}))
#: {"a": 1}
print(Path("report/data.txt").suffix)
#: .txtA lazy import looks like an ordinary one, with
lazy in front, and once loaded the names behave
like eagerly imported ones. json and
pathlib load at the json.dumps and
Path(...) calls. The output is the same either way,
so this listing cannot show the deferral; the next one does.
Before 3.15, deferring a costly import meant moving it inside
the function that needed it. That works, but it hides the
dependency: nothing at the top of the file mentions the module,
tools that read imports miss it, and the import
statement re-runs its sys.modules lookup on every
call. lazy import keeps the declaration at the top
where a reader and a tool can see it, and pays the loading cost
once, at first use. Packages deferred submodule loading before
lazy import existed, by giving
__init__.py a module-level __getattr__
(PEP 562) that
imports and returns a submodule the first time a caller asks for
it by name. The pandas and numpy packages use that technique to
keep import time low. lazy import needs no
hand-written __getattr__, and defers any imported
name, not only a package’s submodules. The
__getattr__ pattern still matters for code that
must run on a Python older than 3.15. You can watch
lazy defer the load by importing a module whose
body prints when it runs:
# noisy.py
print("noisy module loaded")
def announce():
print("noisy.announce() called")# lazy_noisy.py
lazy import noisy
print("before first use")
#: before first use
noisy.announce()
#: noisy module loaded
#: noisy.announce() called
print("after first use")
#: after first useNothing loads at the lazy import line. The body
of noisy runs at noisy.announce(), the
first access, so noisy module loaded prints after
before first use. If a lazily imported module is
missing or broken, the error surfaces at that first use rather
than at the import line. sys.lazy_modules holds the
names still waiting to load, but CPython’s own interpreter
startup adds lazy imports of its own to that set before your
code marks anything lazy:
# lazy_modules_check.py
import sys
lazy import noisy
print(len(sys.lazy_modules) > 1)
#: True
print("noisy" in sys.lazy_modules)
#: True
noisy.announce()
#: noisy module loaded
#: noisy.announce() called
print("noisy" in sys.lazy_modules)
#: FalseThe set already holds more than one name before this script
marks noisy lazy, so sys.lazy_modules
is not a clean “what my program deferred” list.
noisy leaves the set once
noisy.announce() loads it, so the set tracks only
names still waiting, not names your program ever deferred. Check
it for a specific name you marked lazy, rather than reading the
whole set.
lazy works with both import and
from ... import, but only at module scope. Using it
inside a function, a class body, or a try block is
a SyntaxError, and Python likewise rejects
lazy from module import * and a
lazy from __future__ import. To change the setting
for a whole run without editing source, run with
-X lazy_imports=MODE or set
PYTHON_LAZY_IMPORTS=MODE. Both accept one of two
values. normal, the default, defers only the
imports you marked lazy. all defers
every module-level import the keyword could have marked, so the
imports it cannot mark, such as one inside a try
block, stay eager. The PEP also describes a third value,
none, a global off switch. CPython removed it
before the 3.15 release.
Don’t make an import lazy when you import a module for what
its body does rather than for a name it defines. A module that
registers a plugin class (Factory),
installs a codec, or fills a table does that work as it loads.
If nothing touches the lazily imported name, the module stays
unloaded and its registration is missing. The failure is silent:
no error, just a table with a row missing. That is also why
all is an experiment to run rather than a setting
to leave on, since it defers ordinary import
statements too, including the ones whose only purpose is to run
the module.
a_package/module5.py, with
its own function5() and a top-level
print() so the module announces itself when it
loads. Import it three ways, using
import a_package.module5,
from a_package import module5, and
from a_package.module5 import function5, and
confirm the loading message prints only once however many of the
three you use together.a_package/b_package/module6.py with a
function6() that calls function5()
from module5. Import and call
function6() from a script outside
a_package, then rename b_package to
bPackage (and rename it back afterward) and
explain, from the rules in File Names,
why that name is a poor choice even though the import still
works.noisy2.py whose top-level
body prints a message, like noisy.py. In a new script,
lazy import both noisy and
noisy2, then use noisy2 before
noisy. Confirm the two loading messages print in
the order you used the modules, not the order you wrote the
lazy import lines.module.py to
Module.py, change use_module.py to
import Module, and update its call to
Module.useful_function(). Run it. Then change the
import back to import module, leaving the file
named Module.py, and run it again. Predict the
result before you run it, then explain what you see, given that
Windows and macOS open module.py and
Module.py as the same file. Look up
PYTHONCASEOK to confirm your explanation.a_package/module4.py to
the absolute import
from a_package.module1 import function1 and confirm
use_module4.py still
works. Then run python a_package/module4.py
directly, both before and after the change. Both fail, with
different errors: explain each, and say why
python -m a_package.module4 works either way.__all__ line from exporting.py. Predict what
star_import.py prints
without it, run it to check, then restore the line.