Modules and Imports
intermediate14 min readLesson 71 of 169
How import really resolves, the __main__ guard, and avoiding circular imports.
A module is any .py file; import finds it on sys.path, executes it
once, and caches it in sys.modules:
# pricing.py
DISCOUNT = 0.1 # module-level constant: UPPER_CASE
def apply(price):
return price * (1 - DISCOUNT)
from pricing import apply # import the function, not the module
import pricing # import the module; use pricing.apply
Two rules keep imports sane:
from x import yfor names you use constantly; plainimport xwhen the module gives context (csv.readerreads better than a barereader).- Never do
from x import *โ it pollutes the namespace and defeats searchability.
The main guard
Code that should run only when a file is executed directly โ not when imported โ belongs under the guard:
def main():
print("report generated")
if __name__ == "__main__": # true only for direct execution
main()
This is what makes a file both a reusable module and a runnable script. Importers get the functions; nobody accidentally triggers side effects.
Circular imports: the smell and the fix
If a.py imports b while b imports a, one of them receives a
half-built module. The real fix is almost always structure: pull the
shared thing (a type, a constant, a helper) into a third module both can
import. Cycles are a design complaint, not a loading-order puzzle.