Python Part I · Module 13 · Modules, Packages and Imports
if __name__ == "__main__": and Why Scripts Use It
Chapter 13.5 ended with a problem it deliberately did not solve. Importing a file runs everything at its top level, which means a file that does real work when you run it does that same work when some other script merely wants one of its functions. The fix is a single line that appears near the bottom of an enormous proportion of Python files, and it is usually copied rather than understood. It is worth understanding, because it is not special syntax — it is an ordinary comparison against an ordinary variable, and once you have seen what that variable holds in each situation the line stops being an incantation.
Intermediate15 min readPythonPart IFundamentalsModulesScripts
Part I · Module 13 · Chapter 13.6 · if __name__ == "__main__":
1. The Problem, Restated
A file that is useful both ways — it has a function worth importing, and it does something worth running:
def count_errors(lines):
"""Return the number of UVM_ERROR lines."""
n = 0
for line in lines:
if "UVM_ERROR" in line:
n += 1
return n
print("checking the sample log...")
print(count_errors(["UVM_ERROR a", "UVM_ERROR b"]))from parse import count_errors
print("report: starting")
print(count_errors(["UVM_ERROR x"]))checking the sample log...
2
report: starting
1report.py wanted one function. It got the function and two lines of output from a check it never asked for, printed before its own first line.
With print this is untidy. With a file whose top level submits forty jobs, importing it submits forty jobs.
2. What __name__ Holds
Python sets a variable in every module, and its value depends on how that module came to be loaded:
print(__name__)__main__Run a file directly and its __name__ is the string "__main__".
Import the same file and it is the module's own name instead:
print("parse.py sees __name__ as:", __name__)import parse
print("run.py sees __name__ as:", __name__)parse.py sees __name__ as: parse
run.py sees __name__ as: __main__One run, two files, two different values. The file you launched is "__main__"; everything it imports is named after itself.
3. The Guard
def count_errors(lines):
"""Return the number of UVM_ERROR lines."""
n = 0
for line in lines:
if "UVM_ERROR" in line:
n += 1
return n
if __name__ == "__main__":
print("checking the sample log...")
print(count_errors(["UVM_ERROR a", "UVM_ERROR b"]))from parse import count_errors
print("report: starting")
print(count_errors(["UVM_ERROR x"]))report: starting
1The two unwanted lines are gone. report.py got exactly what it asked for.
And the same parse.py, run directly, still does its check:
def count_errors(lines):
"""Return the number of UVM_ERROR lines."""
n = 0
for line in lines:
if "UVM_ERROR" in line:
n += 1
return n
if __name__ == "__main__":
print("checking the sample log...")
print(count_errors(["UVM_ERROR a", "UVM_ERROR b"]))checking the sample log...
2One file, two behaviours, decided by how it was started. That is the entire feature.
4. Why It Reads the Way It Does
The condition says if this file is the one that was launched. Which is why the body is the part that should only happen then:
| How the file was started | __name__ | Guard body |
|---|---|---|
python3 parse.py | "__main__" | runs |
import parse from another file | "parse" | skipped |
A useful way to read the line aloud: "if I am the program rather than a part of one."
5. Put the Work in main()
The canonical form does not put the work inline under the guard — it puts it in a function and calls that:
def count_errors(lines):
"""Return the number of UVM_ERROR lines."""
n = 0
for line in lines:
if "UVM_ERROR" in line:
n += 1
return n
def summarise(count):
"""Return a one-line verdict for an error count."""
if count == 0:
return "PASS"
return "FAIL (" + str(count) + " errors)"
def main():
"""Check the built-in sample logs and print a verdict for each."""
logs = {
"axi_smoke": ["INFO ok"],
"axi_burst": ["UVM_ERROR a", "UVM_ERROR b"],
}
for name in sorted(logs):
print(name, "->", summarise(count_errors(logs[name])))
if __name__ == "__main__":
main()axi_burst -> FAIL (2 errors)
axi_smoke -> PASSThree reasons this beats putting the loop directly under the if:
The guard stays one line. A reader sees instantly that the file is runnable and where to look for what it does.
main() can be called by something else — another script, or a check that wants to exercise the whole flow. Code buried under an if can only ever be reached by launching the file.
Names inside main() are local. Chapter 11.10's rule: logs and name do not become module-level names that an importer could stumble into. Under a bare guard they would.
This is Chapter 11.15's architecture in one file — count_errors and summarise are the pure core, main() is the arrangement, and the guard is the edge that decides whether the arrangement runs.
6. The Self-Test Pattern
The guard makes a cheap kind of check possible, and it is genuinely useful before Module 14 gives you anything better:
def count_errors(lines):
"""Return the number of UVM_ERROR lines."""
n = 0
for line in lines:
if "UVM_ERROR" in line:
n += 1
return n
def main():
"""Run the module's own checks."""
checks = [
([], 0),
(["INFO ok"], 0),
(["UVM_ERROR a"], 1),
(["UVM_ERROR a", "UVM_ERROR b"], 2),
]
failures = 0
for lines, expected in checks:
actual = count_errors(lines)
if actual != expected:
failures += 1
print("WRONG:", lines, "->", actual, "expected", expected)
print(len(checks) - failures, "of", len(checks), "checks passed")
if __name__ == "__main__":
main()4 of 4 checks passedRunning the file checks it; importing the file does not. That is Chapter 11.15 Section 6's testable core, with a place to put the tests that costs nothing and ships with the module.
7. What the Guard Does Not Do
Three honest limits:
It does not protect definitions. Everything at the top level that is not inside the guard still runs on import — every def, every constant, every import. That is correct and necessary, because those are the things the importer came for:
MARKER = "UVM_ERROR"
print("config.py: this still runs on import")
def marker():
"""Return the configured marker."""
return MARKER
if __name__ == "__main__":
print("config.py: this does not")from config import marker
print("marker is", marker())config.py: this still runs on import
marker is UVM_ERRORThe unguarded print fired; the guarded one did not.
It is not a privacy mechanism. Nothing is hidden. An importer can still reach every name in the module, including ones you consider internal.
It does not make a file safe to import on its own. A file whose top level deletes a directory is unsafe regardless of what sits under a guard further down. The guard helps only with code you actually put inside it.
8. Predict, Run, Observe
def double(n):
"""Return n doubled."""
return n * 2
print("lib: top level")
if __name__ == "__main__":
print("lib: guarded")import lib
print("app:", lib.double(21))
print("app: __name__ is", __name__)
print("lib: __name__ is", lib.__name__)lib: top level
app: 42
app: __name__ is __main__
lib: __name__ is liblib: top level printed, because it is not inside the guard — and it printed first, during the import.
lib: guarded did not print at all. lib was imported, so its __name__ was "lib", not "__main__".
The two __name__ values are the whole mechanism side by side: the launched file is "__main__", the imported one is named after itself.
9. Exercises
Exercise 1 — see both values. Write two files where each prints its own __name__, and run the importer. Say which value each got and why.
Exercise 2 — add the guard. Take Section 1's noisy parse.py and guard it. Confirm the importer's output is clean and that running parse.py directly still works.
Exercise 3 — read the line. Translate if __name__ == "__main__": into plain English in one sentence, without using the word __name__.
Exercise 4 — main() or inline. Write the same guarded file twice — once with the work inline under the if, once in main(). Give two reasons to prefer the second.
Exercise 5 — local names. Put a loop variable under a bare guard and confirm it becomes a module-level name. Then move it into main() and confirm it does not. Name the chapter whose rule explains this.
Exercise 6 — a self-test. Add a main() to a module of your own that checks one function against four cases and reports how many passed. Confirm importing it prints nothing.
Exercise 7 — what still runs. Write a module with a constant, a def, an unguarded print and a guarded print. Import it and list exactly which of the four executed.
Exercise 8 — when to skip it. Name one kind of file that does not need the guard and one that does, and justify each in a sentence.
10. Summary
- Importing a file runs its top level, which is the problem the guard solves.
__name__is an ordinary variable holding a string. The launched file's is"__main__"; an imported module's is its own name.- The guard is a plain
ifwith a plain==— Chapter 9.3's comparison, not special syntax. - Read it as "if I am the program rather than a part of one."
- One file can then behave two ways, decided by how it was started.
- Put the work in
main()and call it from the guard: the guard stays one line,main()stays callable by other code, and its names stay local. - That is Chapter 11.15's architecture in one file — pure core,
main()as arrangement, guard as the edge. - A self-test under the guard ships with the module and costs nothing.
- The guard does not protect definitions — every
def, constant andimportat the top level still runs on import, which is what the importer wanted. - It is not privacy, and it does not make an unsafe top level safe.
The last chapter of the module groups several files into something importable as a unit.
Where this fits
Part of the Python curriculum.
