Skip to content
VLSI Mentor

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:

Azvya Education Pvt. Ltd.VLSI Mentor
parse.py
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"]))
Azvya Education Pvt. Ltd.VLSI Mentor
report.py
from parse import count_errors

print("report: starting")
print(count_errors(["UVM_ERROR x"]))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
checking the sample log...
2
report: starting
1

report.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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(__name__)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
__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:

Azvya Education Pvt. Ltd.VLSI Mentor
parse.py
print("parse.py sees __name__ as:", __name__)
Azvya Education Pvt. Ltd.VLSI Mentor
run.py
import parse

print("run.py sees __name__ as:", __name__)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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

Azvya Education Pvt. Ltd.VLSI Mentor
parse.py
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"]))
Azvya Education Pvt. Ltd.VLSI Mentor
report.py
from parse import count_errors

print("report: starting")
print(count_errors(["UVM_ERROR x"]))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
report: starting
1

The two unwanted lines are gone. report.py got exactly what it asked for.

And the same parse.py, run directly, still does its check:

Azvya Education Pvt. Ltd.VLSI Mentor
parse.py
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"]))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
checking the sample log...
2

One 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:

Azvya Education Pvt. Ltd.VLSI Mentor
check.py
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()
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
axi_burst -> FAIL (2 errors)
axi_smoke -> PASS

Three 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:

Azvya Education Pvt. Ltd.VLSI Mentor
parse.py
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()
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
4 of 4 checks passed

Running 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:

Azvya Education Pvt. Ltd.VLSI Mentor
config.py
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")
Azvya Education Pvt. Ltd.VLSI Mentor
use.py
from config import marker

print("marker is", marker())
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
config.py: this still runs on import
marker is UVM_ERROR

The 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

Azvya Education Pvt. Ltd.VLSI Mentor
lib.py
def double(n):
    """Return n doubled."""
    return n * 2


print("lib: top level")

if __name__ == "__main__":
    print("lib: guarded")
Azvya Education Pvt. Ltd.VLSI Mentor
app.py
import lib

print("app:", lib.double(21))
print("app: __name__ is", __name__)
print("lib: __name__ is", lib.__name__)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
lib: top level
app: 42
app: __name__ is __main__
lib: __name__ is lib

lib: 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 if with 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 and import at 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.