SECTION 1
Historical Context & Motivation
In the earliest days of programming, a program was just one long file of instructions executed from top to bottom. As software became more complex, developers realized they needed a way to split code into smaller, reusable pieces. Python, created by Guido van Rossum in the late 1980s, adopted the concept of modules from earlier languages to solve this exact problem. Modules let you write a function once and reuse it across many programs, rather than copying and pasting code everywhere.
1960s • Subroutine Libraries
1991 • Python 0.9 Released
import statement, inspired by Modula-3's clean module design.2000 • Python 2.0 & Packages
__init__.py files, making large-scale code organization standard practice.2008 • Python 3 & Modern Imports
if __name__ == '__main__' guard a widely taught best practice for writing dual-purpose files.2020s • PyPI Ecosystem Explodes
The core question that drove all of this evolution is simple: How can Python tell whether a file is being run directly by a user, or being borrowed by another file? The answer lies in a special variable called __name__, and understanding it is one of the most important steps in learning to write clean, reusable Python code.
SECTION 2
Core Principles & Definitions
Before diving deeper, you need to understand a few foundational ideas. Every .py file you create in Python can serve two roles: it can act as a script (a program you run directly) or as a module (a toolbox of functions and variables that another file imports). The same file can even play both roles depending on how it is used.
Script
python my_file.py, that file is acting as a script. Python executes every line from top to bottom.Module
import statement. The importing file borrows functions, classes, or variables from the module without running the module's main logic.The __name__ Variable
__name__ is set to "__main__". If the file is imported, __name__ is set to the module's filename (without .py).The Main Guard
if __name__ == '__main__': is called the main guard. Code inside this block runs only when the file is executed as a script, not when it's imported as a module.KEY TAKEAWAY
__name__ variable is like a label that tells you whether you're the one cooking (it says __main__) or someone else borrowed your recipe (it says the card's name instead).SECTION 3
Visual Explanation
The diagram below shows exactly what happens when Python encounters a file. The key difference is how Python sets the __name__ variable depending on whether you run the file directly or import it from another file.
python helpers.py directly, Python sets __name__ to "__main__" (left path). When another file writes import helpers, Python sets __name__ to "helpers" (right path). The main guard uses this difference to decide what code to run.Notice the two paths diverge at the very beginning. Python makes this decision before any of your code runs. By the time the first line of your file executes, __name__ has already been set. The main guard simply checks which value was assigned and decides whether to run certain blocks of code.
SECTION 4
How It Works Under the Hood
When you type python my_file.py in the terminal, Python's interpreter does several things behind the scenes before executing your code. Understanding this sequence makes the __name__ behavior feel completely logical rather than magical.
Step-by-Step: Running a File Directly
- Step 1: Python reads the file and creates a special namespace (a container for all variables and functions defined in the file).
- Step 2: Python sets the built-in variable
__name__to the string"__main__"because this is the entry point — the file you asked Python to run. - Step 3: Python executes every line of the file, top to bottom, including any
if __name__ == '__main__':block (because the condition is True).
Step-by-Step: Importing a File as a Module
- Step 1: The importing file encounters
import helpers. Python findshelpers.pyand reads it. - Step 2: Python sets
__name__to"helpers"(the module's filename without the .py extension). - Step 3: Python executes the module's code top to bottom, but the
if __name__ == '__main__':block is skipped because"helpers" != "__main__". - Step 4: All function definitions, class definitions, and top-level variables from the module become available in the importing file.
Important Detail
print() statements or variable assignments at the top level of the module will still run during import. That's exactly why the main guard exists — to protect code that should only run when the file is used as a script.The value of __name__ is always a string. There are only two possibilities: it is either "__main__" (run directly) or the module's own name (imported). This simple, binary distinction is what makes the pattern so powerful and reliable.
SECTION 5
Anatomy of a Dual-Purpose Python File
A well-structured Python file that can work as both a script and a module follows a predictable layout. The diagram below shows the anatomy of such a file, highlighting which parts execute in each mode.
app.py, only the imports and function definitions execute. The script-only code inside the main guard is skipped entirely.The key insight is that function definitions are just instructions for creating functions — they don't actually do anything until someone calls them. When app.py imports math_tools, Python reads the def statements and makes those functions available. But because __name__ is "math_tools" (not "__main__"), the print() statement never runs. This is exactly the behavior you want.
SECTION 6
Worked Example
Let's walk through a concrete scenario. You have two files: greetings.py (which defines a function) and main_app.py (which imports it). We'll trace what happens in both cases.
Tracing __name__ Across Two Files
- 1
Step 1 — Write greetings.py
Create a file calledgreetings.pywith the following code:def say_hello(name):return f"Hello, {name}!"print(f"greetings.py: __name__ is {__name__}")if __name__ == "__main__":message = say_hello("Alice")print(message) - 2
Step 2 — Run greetings.py Directly
In the terminal, typepython greetings.py. Python sets__name__to"__main__". The diagnosticprintruns (it's outside the guard), then the guard condition is True, so the greeting prints too.Output:greetings.py: __name__ is __main__Hello, Alice! - 3
Step 3 — Write main_app.py
Create a second file calledmain_app.py:import greetingsprint(greetings.say_hello("Bob")) - 4
Step 4 — Run main_app.py
Typepython main_app.py. When Python encountersimport greetings, it readsgreetings.pyand sets its__name__to"greetings". The diagnostic print on line 4 still runs (it's outside the guard), but the main guard block is skipped. Thenmain_app.pycalls the function and prints its own greeting.Output:greetings.py: __name__ is greetingsHello, Bob! - 5
Step 5 — Key Observation
Notice that"Hello, Alice!"does NOT appear when runningmain_app.py. The main guard successfully prevented the test code from running during import. However, the unguardedprintstatement on line 4 still executed — this demonstrates why you should put anything you don't want running on import inside the main guard.The main guard acts as a gatekeeper, only allowing script-specific code through when the file is the entry point.
SECTION 7
Script vs. Module — Side-by-Side Comparison
Now that you understand both modes, let's put them side by side. The table below highlights every key difference between using a Python file as a script versus as a module.
| Feature | Script Mode | Module Mode |
|---|---|---|
| How it starts | python filename.py | import filename |
| __name__ value | "__main__" | The file's name (e.g., "filename") |
| Top-level code runs? | Yes, everything executes | Yes, everything outside the guard executes |
| Main guard code runs? | Yes (condition is True) | No (condition is False) |
| Primary purpose | Execute a program | Provide reusable functions/classes |
| Typical use case | Running a game, app, or automation task | Sharing utility functions between projects |
Key differences between Python script mode and module mode
KEY TAKEAWAY
if __name__ == '__main__': guard is the switch that controls which tools are exposed.Common Beginner Mistake
if __name__ == '__main__':.SECTION 8
Connection to Packages and Advanced Organization
Once you master modules and the main guard, the next step is understanding packages. A package is simply a folder of related modules, bundled together with a special file called __init__.py. Packages let you organize large projects into logical groups, like chapters in a book. The same __name__ mechanism works within packages, but the names become dotted paths like "mypackage.helpers".
| Concept | What You Learn Now | What Comes Next |
|---|---|---|
| Code organization | Single file as script or module | Packages (folders of modules) |
| __name__ value | "__main__" or module name | Dotted package paths like "pkg.mod" |
| Running packages | python file.py | python -m package (using __main__.py) |
| Import style | import module | from package import module |
| Third-party code | Standard library modules | Installing packages from PyPI with pip |
From modules to packages: building on what you know
You might also encounter python -m module_name in tutorials. The -m flag tells Python to find the module in its search path and run it as a script. Even in this case, __name__ is set to "__main__", so the main guard still works exactly as expected. Understanding the basics of scripts and modules gives you a strong foundation for these more advanced tools.
SECTION 9
Practice Problems
Question 1: CONCEPTUAL
.py file be both? Why or why not?Show answer
python my_file.py), while a module is a Python file that another file imports to use its functions or variables. Yes, a single .py file can absolutely be both. The file doesn't change — only how it is used changes. By including a if __name__ == '__main__': guard, you can design a file that provides reusable functions when imported and also runs useful standalone behavior when executed directly.Question 2: BASIC
utils.py that contains the following code:
def double(x):
return x * 2
print("utils loaded")
if __name__ == "__main__":
print(double(7))
What is the exact output when you run python utils.py?Show answer
utils loaded
14
First, Python defines the double function (no output from a definition). Then it executes print("utils loaded") because that line is at the top level, outside any guard. Finally, since the file is run directly, __name__ equals "__main__", so the guard passes and print(double(7)) outputs 14.Question 3: INTERMEDIATE
utils.py from Problem 2, now you create a file called app.py with this code:
import utils
print(utils.double(5))
What is the exact output when you run python app.py? Explain why each line appears (or doesn't appear).Show answer
utils loaded
10
When app.py runs import utils, Python executes utils.py with __name__ set to "utils". The print("utils loaded") line is outside the guard, so it runs and produces the first line. The main guard condition ("utils" == "__main__") is False, so print(double(7)) is skipped — notice 14 does NOT appear. Then app.py calls utils.double(5) and prints 10.Question 4: APPLIED
converter.py that defines a celsius_to_fahrenheit(c) function. When run directly, it should convert 100°C and print the result. When imported by your main project file, it should not print anything. Write the complete code for converter.py.Show answer
def celsius_to_fahrenheit(c):
return c * 9/5 + 32
if __name__ == "__main__":
result = celsius_to_fahrenheit(100)
print(f"100°C = {result}°F")
The function definition is outside the guard, so it's available whether the file is run directly or imported. The test code that prints the conversion is inside the guard, so it only runs when you execute python converter.py directly. When another file writes import converter, it gains access to converter.celsius_to_fahrenheit() without any output being printed.Question 5: CRITICAL THINKING
Show answer
if __name__ == '__main__': is the program's entry point. This is a widely recognized Python pattern, and using it makes your code more readable and professional. Without it, there's no standard way to distinguish "code that tests the module" from "code that IS the program." The guard provides both functional protection and clear documentation of intent.SUMMARY
Lesson Summary
Summary
Every Python .py file can serve as a script (run directly) or a module (imported by another file). Python distinguishes between these two roles using the built-in __name__ variable. When a file is run directly, __name__ is automatically set to "__main__". When a file is imported as a module, __name__ is set to the module's filename (without the .py extension).
The main guard pattern — if __name__ == '__main__': — uses this distinction to protect code that should only execute in script mode. Function and class definitions should live outside the guard so they're always available, while test code, print() statements, and program logic go inside it. This pattern lets you create dual-purpose files that work both as standalone programs and as reusable libraries — a fundamental skill for writing clean, professional Python code.