Why this matters
As soon as a program grows past a page or two, you want to split it into files: one for parsing input, one for the calculations, one for output. And you will often write a file that is useful both as a standalone program and as a library of functions for other code, including your tests. Python has one small idiom that makes this work, if __name__ == "__main__":, and you will see it at the bottom of almost every Python script. This lesson explains what it means and how to organize a module so importing it is safe.
Splitting a program into modules
Suppose a program reads scores and prints a summary. You can put the reusable logic in its own file:
# scoring.py
"""Functions for summarizing test scores."""
PASS_MARK = 50
def passed(scores):
"""Return the scores that are at or above the pass mark."""
return [s for s in scores if s >= PASS_MARK]
def summary(scores):
"""Return a one-line summary of a list of scores."""
ok = passed(scores)
return f"{len(ok)} of {len(scores)} passed, best {max(scores)}"
and keep the program itself small:
# main.py
import scoring
scores = [int(x) for x in input().split()]
print(scoring.summary(scores))
$ echo "40 75 50 90" | python3 main.py
3 of 4 passed, best 90
scoring.py begins with a module docstring, a string literal as the first statement. It becomes scoring.__doc__ and is what help(scoring) shows. Every function has one too. The module only defines things: one constant and two functions. Importing it has no visible effect, which is exactly what you want from a library.
Every module knows its name: name
When Python creates a module object, it sets a few special names in its namespace, called dunder names (for double underscore). The most useful is __name__:
- For an imported module,
__name__is the module's name:"scoring". - For the file you run directly with
python3 file.py, the module is named"__main__"instead.
# whoami.py
print("my __name__ is", repr(__name__))
$ python3 whoami.py
my __name__ is '__main__'
$ python3 -c "import whoami"
my __name__ is 'whoami'
Underneath, running python3 whoami.py makes the interpreter create a module called __main__, put it in sys.modules["__main__"], and execute whoami.py's code in it. Importing creates a module named after the file. Same code, same machinery, different name.
The idiom
Because __name__ tells you how the file is being used, you can make part of it run only when it is the main program:
# scoring.py
"""Functions for summarizing test scores."""
PASS_MARK = 50
def passed(scores):
return [s for s in scores if s >= PASS_MARK]
def summary(scores):
ok = passed(scores)
return f"{len(ok)} of {len(scores)} passed, best {max(scores)}"
def main():
scores = [int(x) for x in input().split()]
print(summary(scores))
if __name__ == "__main__":
main()
Now the file works both ways:
$ echo "40 75 50 90" | python3 scoring.py
3 of 4 passed, best 90
$ python3 -c "import scoring; print(scoring.passed([10, 60]))"
[60]
When imported, the final if is false, so main() is never called: no input(), no output. When run, the if is true and the program starts.
Why put the program in a main() function rather than directly under the if? Three reasons:
- Local variables. Code at module level creates global names (
scoreswould becomescoring.scores), which can accidentally be used by other functions. Insidemain(), they are local and disappear when it returns. - Testability. A test can call
main()itself, with stdin replaced, without starting a separate process. - Readability. The reader sees "definitions first, then one line that starts everything" and knows where the program begins.
A common extension is to have main return an exit status, sys.exit(main()), which you met with exit codes in the previous module.
What runs at import time
Remember that importing a module runs every top-level statement in it, once. So think about each top-level line as "something that happens to anyone who imports me":
| At top level | Effect when imported | OK? |
|---|---|---|
def f(): ... | creates a function object, binds f | yes |
class C: ... | creates a class | yes |
TIMEOUT = 30 | binds a constant | yes |
import json | imports (or reuses) json, binds json | yes |
print("loaded") | prints, in every importer, every test run | no |
data = open("big.csv").read() | reads a file that may not exist where the importer runs | no |
name = input() | blocks, waiting for a user who is not there | no |
Code that does things belongs in functions. The rule of thumb: importing a module should define names and nothing else. Tools depend on this: test runners import your files to find tests, documentation generators import them to read docstrings, and editors import them for autocompletion.
How the pieces of a project fit together
A typical small project:
wordtool/
wordtool.py the command-line program: parses arguments, calls the library
textstats.py pure functions: count_words(text), top_words(text, n)
test_textstats.py tests that import textstats
textstats.py has no input() or print(): it takes data as arguments and returns results. wordtool.py handles the outside world (stdin, files, output). This split, often called "functional core, imperative shell", makes the logic easy to test and reuse. You will lean on it in the testing module.
Common mistakes
- Typos in the guard.
if __name__ == "main":orif name == "__main__":never runs (or raisesNameError). It must be__name__(two underscores each side) compared with"__main__". - Forgetting to call
main(). Definingdef main():without the guard and the call means the script silently does nothing when run. - Running the guard's code at import because it is not indented. Only the lines indented under the
ifare guarded. - Hidden dependence on globals. Code like
def summary(): return len(scores)that relies on a module-levelscoresworks when run as a script and fails withNameErrorwhen imported. Pass data in as arguments. - Two copies of one module. If
scoring.pyis run as the main program and imported by another of your modules, it exists twice: once as__main__and once asscoring, each with its own globals. Keep entry-point scripts thin so this does not matter.
Recap
- Your own modules are just
.pyfiles next to your script; give them a docstring and keep them focused. __name__is the module's name when imported, and"__main__"for the file being run.if __name__ == "__main__": main()lets one file be both a library and a program.- Importing should only define names; put actions (input, output, file access) inside functions.
Going further: run python3 -c "import json; print(json.__name__, json.__file__)", then open that file and look at the bottom of json/tool.py in the same directory for a standard-library example of the idiom.