Modules and Imports
A module is simply a .py file. Anything defined in it (functions, classes, variables) becomes accessible to other files via import.
Import Styles
import math # import the whole module, access via math.sqrt()
math.sqrt(16)
import math as m # alias, useful for long/conflicting names
m.sqrt(16)
from math import sqrt # import a specific name directly into your namespace
sqrt(16)
from math import sqrt, pi # import multiple specific names
from math import * # import everything (avoid, see below)Avoid
from module import *This pollutes your namespace with every public name from that module, making it unclear where a given name came from, and risking silent overwrites of your own variables/functions with the same name. Always import explicitly.
How Python Finds Modules
When you import foo, Python searches, in order:
- The directory of the script being run.
- Directories listed in the
PYTHONPATHenvironment variable. - Standard library directories.
- Site-packages (where
pip-installed packages live).
import sys
print(sys.path) # the actual list of directories searched, in orderModule Caching: Imports Only Run Once
# my_module.py
print("my_module is being loaded")
x = 42import my_module # prints 'my_module is being loaded'
import my_module # prints NOTHING, already cached in sys.modules, not re-executedInfo
sys.modulesis a dict cache of every module already imported in the current process. Repeatedimportstatements for the same module are essentially free after the first one.
Reloading a Module (Rare, Mostly for Interactive Development)
import importlib
import my_module
importlib.reload(my_module) # forces re-execution, useful in REPL/notebook workflowsRelative vs Absolute Imports (Within a Package)
# project/
# package/
# __init__.py
# module_a.py
# module_b.py
# Inside module_b.py:
from package.module_a import some_function # absolute import, unambiguous, PREFERRED
from . import module_a # relative import, . means "this package"
from .module_a import some_function # relative import of a specific name
from .. import sibling_package # .. means "one level up"Prefer absolute imports for clarity
Absolute imports (
from package.module_a import x) are explicit about exactly where something comes from and behave predictably regardless of how the script is run. Relative imports (from . import x) only work correctly when the file is part of a properly recognized package, and can break when a file is run directly as a script.
Circular Imports
# a.py
from b import func_b
def func_a():
return func_b()
# b.py
from a import func_a # ImportError! a.py hasn't finished loading func_a yet
def func_b():
return func_a()Circular import fix strategies
- Restructure code to remove the mutual dependency (often the real fix, indicates poor separation of concerns).
- Move the import inside the function body instead of the top of the file, deferring it until it’s actually called.
- Merge the two modules if they are this tightly coupled.
__init__.py: Making a Directory a Package
mypackage/
__init__.py
module_a.py
module_b.py
__init__.py can be empty (just marks the directory as a package) or can control what gets exposed:
# mypackage/__init__.py
from .module_a import function_a
from .module_b import function_b
__all__ = ["function_a", "function_b"] # controls `from mypackage import *` behaviorfrom mypackage import function_a # works because __init__.py re-exported itNamespace packages
Since Python 3.3,
__init__.pyis technically optional for a directory to be treated as a package (namespace packages). In practice, most real projects still include it for explicit control over what the package exposes.
Running a Module as a Script
python -m module_name # runs module_name as __main__, respects package imports correctly
python module_name.py # runs the file directly, but package-relative imports can break
python -mis usually the safer way to run project codeThis matters especially for scripts inside a package that use relative imports internally,
python -m package.scriptresolves correctly wherepython package/script.pyoften does not.