Definition
A Python file can be read in two different ways: run directly from a terminal, or imported by another file that only wants one function from it. The trouble is that Python makes no difference between the two by default, for lack of a mandatory function called main as in C or Java: running a file simply means executing its lines from top to bottom, definitions included.
That is what the if __name__ == "__main__" guard solves. Sitting at the bottom of the file might suggest it starts the program, but it actually separates what must run on launch from what should stay available on import.
def add(a, b):
return a + b
if __name__ == "__main__":
print(add(2, 3)) # only runs when the file is launched directlyRun directly, this file prints 5. Imported with import, it prints nothing: it merely offers its function add. The code is identical, only the usage changes.
What __name__ holds in each situation
Before running a file, Python drops a special variable into it, __name__. One might expect it to always hold the same thing; it is the opposite that makes the mechanism useful. Here is what it holds depending on how the file was loaded.
| Situation | Content of __name__ |
|---|---|
python tools.py | "__main__" |
| The same file imported elsewhere | "tools" |
python -m tools | "__main__" |
| Typed straight into the REPL | "__main__" |
| File loaded by pytest | "test_tools" |
The launched file is always called __main__, every other one keeps its usual module name. The comparison therefore only asks whether this file is being launched or merely loaded.
The problem it actually solves
Without that guard, importing a file runs the whole file, not just its definitions: the calls, the printed output, the file being opened, the network request being sent all come along with them, the day someone imports a script to reuse a single function.
# cleanup.py, no guard
def clean(path):
...
clean("data.csv") # also runs on import, whether that was wanted or notThe costliest case involves multiprocessing. On Windows and on macOS, it cannot duplicate the running process the way Linux does: it restarts each child process by re-importing the main file.
Without a guard, that re-import restarts the process creation, which itself re-imports the file and creates more processes in turn. The system eventually forces this avalanche to stop, with an error message mentioning freeze_support and never naming the real cause.
The shape to write, and why that one
It is better not to put the whole program inside the guard, but to keep a single call there, one that calls a main function holding the rest.
def main():
config = load_config()
process(config)
if __name__ == "__main__":
main()The reason comes down to scope. Whatever is written inside the block belongs to the module: every name becomes a global variable, changeable by accident from anywhere in the file. Wrapped inside a function, that same code keeps its names to itself, becomes testable, and attachable to a terminal command, which is exactly the second meaning of the term.
The other meaning: the installed command
The same term covers a second thing, often confused with the first. A published package can declare entry points in its configuration file: each one creates a terminal command that calls one precise function of the project. That is how pip installs commands usable without typing python in front of them.
pip install ruff
ruff check .The two notions resemble each other without replacing each other, hence the value in knowing which one is being looked for.
| Point of comparison | The guard in the file | The project declaration |
|---|---|---|
| Where it is written | At the bottom of the script concerned | In the configuration file of the package |
| What it produces | A file behaving differently when launched and when imported | A command available in the terminal after installation |
| When it earns its place | As soon as a file is both a script and a library | When a project has to be used like a real tool |
When the guard is useless
It is neither magic nor mandatory. A file of functions never launched directly has nothing to protect. A throwaway ten-line script, written to run once and never imported, does not need it either.
The classic mistake is copying it everywhere without knowing why. The mirror mistake is forgetting it when it matters: a file that grows, that a colleague eventually imports, or that a test suite loads to check a single function. The right reflex is to ask, once per file, whether anyone could ever import it.
Frequently asked questions
Is a main.py file required as in other languages?
No, the file name means little to Python: whichever file is passed to the command becomes the entry point, whatever it happens to be called. The one exception is the __main__.py file placed inside a package folder, which makes that folder runnable with python -m mypackage and acts as the official door of the project.
Why does my script seem to run twice?
Because the same file is loaded under two identities: once as the launched file, under the name __main__, and once as an imported module, under its real name. Python then keeps two separate copies in memory, with two independent sets of global variables. The guard is not always enough: a launched file must also avoid ending up importing itself.
What happens when the quotes or the underscores are written wrong?
Nothing visible, which is exactly what makes the mistake expensive. Writing if __name__ == "main" produces a valid comparison that is always false: the block never runs, and no error is printed. A typo on the variable itself is louder, since an unknown name raises a NameError. So count two underscores on each side of the word, and check the exact word sitting between the quotes.