Yesterday you split the inventory helpers into their own module and imported them from main.py. What happened the first time you ran the module file directly to test a function?
The whole thing ran. I had a test call at the bottom — print(normalize_name("WIDGET-A ")) — just to check the output. When I ran the module directly it printed. Fine. But then I imported the module from main.py and it printed again. Middle of the dashboard startup, out of nowhere: widget-a.
Right. And that's the exact problem if __name__ == '__main__': solves. Think about the warehouse's two visitor modes.
Two visitor modes?
A logistics partner arrives through the loading dock — they want to pull inventory, fill orders, nothing else. They don't need a tour. A retail buyer visits for a showroom walkthrough — sample tables out, products displayed, guided tour running. Same building, same inventory, completely different behavior based on who walked in.
And a Python file is the same building. When you run it directly, you're the retail buyer — the full experience fires. When you import it, you're the logistics partner — just pull the functions, don't set off the alarms.
That's exactly it. Python sets a special variable called __name__ every time it runs a file. When you execute a file directly, __name__ is the string '__main__'. When you import that same file as a module, __name__ is the module's filename without the .py:
# inventory_utils.py
def normalize_name(name: str) -> str:
return name.strip().lower()
def count_low_stock(products: list[dict], threshold: int = 10) -> int:
return sum(1 for p in products if p["stock"] < threshold)
if __name__ == '__main__':
# Only runs when you execute this file directly.
# Importing this module never touches this block.
print(normalize_name('WIDGET-A '))
print("Direct execution — dev/test mode")Python sets __name__ automatically? Before any of my code runs?
Automatic, every time, no exceptions. Python sets it before a single line of your code runs. Execute python inventory_utils.py — Python sets __name__ = '__main__' in that file's namespace. main.py does import inventory_utils — Python sets __name__ = 'inventory_utils' instead. The guard reads that variable and decides whether to run the block.
So the functions are always available on import — normalize_name, count_low_stock — because they're defined at the top level outside the guard. Only the code inside the guard is conditional.
Correct. Function definitions at the top level always execute on import — that's how Python registers them. The guard doesn't hide your functions from importers. It only gates code that should run in script mode: test calls, setup, CLI entry points.
The test print I had yesterday — I just needed to put it inside the guard. One line of protection, problem gone.
One line. But I want to show you the other direction — what a well-structured script looks like when you use the guard intentionally, not just defensively:
# run_report.py
import csv
from inventory_utils import normalize_name, count_low_stock
def load_products(filepath: str) -> list[dict]:
with open(filepath, newline='') as f:
reader = csv.DictReader(f)
return [
{**row, "name": normalize_name(row["name"]), "stock": int(row["stock"])}
for row in reader
]
def print_report(products: list[dict]) -> None:
low = count_low_stock(products)
print(f"Total products: {len(products)}")
print(f"Low stock alerts: {low}")
if __name__ == '__main__':
products = load_products("warehouse.csv")
print_report(products)The functions are importable — someone could do from run_report import load_products and reuse it. But the actual report only runs when you execute the file. That's the showroom vs. loading dock split, right there in the code.
And this pattern scales to every serious Python project. Command-line tools, Django management commands, data pipeline scripts — they all use this pattern. The functions are importable by other Python code; the CLI fires only when you run the script directly.
Why '__main__' specifically? Is that string reserved?
Python hardcodes that string as the sentinel for direct execution. The double underscores are a Python convention for names the interpreter manages: __name__, __file__, __doc__, __init__ — called "dunder" attributes. They signal: Python itself manages this, not you.
Dunder. That sounds like a hobbit name. But I've been writing __init__ in empty package directories without knowing why — now it makes sense. It's a Python-managed name.
You'll see __name__ in a few other contexts too, but the core mechanic is always the same: Python sets it, you read it, you branch on it. Now let's look at what your challenge today actually analyzes:
def analyze_script(lines: list[str]) -> dict:
has_main_guard = False
function_count = 0
import_count = 0
would_run_on_import = []
for line in lines:
stripped = line.strip()
if stripped == "if __name__ == '__main__':" or stripped == 'if __name__ == "__main__":':
has_main_guard = True
elif stripped.startswith("def "):
function_count += 1
elif stripped.startswith("import ") or stripped.startswith("from "):
import_count += 1
elif stripped and not stripped.startswith(("def ", "class ", "#", "if __")):
would_run_on_import.append(stripped)
return {
"has_main_guard": has_main_guard,
"function_count": function_count,
"import_count": import_count,
"would_run_on_import": would_run_on_import,
}Static analysis — reading the lines as text, not executing them. It checks whether the guard exists, counts functions, counts imports, and flags everything else at the top level as code that fires on import.
Simplified, but that's the real idea behind pylint, flake8, mypy. They parse your source file and reason about it without running it. You're doing the same thing in miniature.
The would_run_on_import list is the interesting one. That's the code that will surprise whoever imports this module — the stuff that probably belongs inside the guard but isn't there yet.
Did you go back and put the Monday report's test calls inside the guard after yesterday?
I did. Diane said startup was cleaner. That's the highest praise I've ever received from Diane.
Tomorrow: packages. You've been putting files in the same directory. What happens when your project grows and you need to organize files into subdirectories — and have Python treat those subdirectories as organized namespaces? You've already seen __init__.py. Tomorrow that becomes intentional.
So a package is a directory Python knows about, and __init__.py is what makes it a package instead of just a folder?
That's the shape of it. But __init__.py does more than exist — it controls what the package exposes when someone writes from inventory import normalize_name. Tomorrow you design the import interface for a package, not just a module.
Every time Python runs a file — whether executed directly or imported — it sets the __name__ variable in that file's namespace:
python my_script.py): __name__ == '__main__'import my_script): __name__ == 'my_script'The guard if __name__ == '__main__': makes a block of code conditional on direct execution. Code outside the guard runs on both import and direct execution.
# inventory_utils.py
def normalize_name(name: str) -> str:
return name.strip().lower()
if __name__ == '__main__':
# Only runs when you execute this file directly
print(normalize_name('WIDGET-A ')) # prints: widget-a
# Importing this file never reaches hereWhy the guard matters:
"Dunder" convention: Names surrounded by double underscores (__name__, __file__, __doc__, __init__) are managed by Python itself. You read them; you don't create them.
Pitfall 1: Missing the guard on modules with side effects. import my_module executes the file. Any top-level print, network call, database connection, or file open outside the guard fires immediately on import. Always put executable behavior inside if __name__ == '__main__':.
Pitfall 2: Putting functions inside the guard. Functions defined inside the guard are not importable — they're only created during direct execution. Top-level def blocks are always importable, regardless of the guard.
Pitfall 3: Confusing __name__ with the filename. On import, __name__ is the module name (no .py), not the file path. For a file at utils/inventory.py imported as utils.inventory, __name__ is 'utils.inventory', not 'inventory' or the full path.
The if __name__ == '__main__': pattern pairs naturally with a main() function:
def main() -> None:
products = load_products("warehouse.csv")
print_report(products)
if __name__ == '__main__':
main()This makes the entry point itself importable — useful for testing and for tools that call your main() programmatically. The __main__.py file in a package lets the package run as a script with python -m package_name, setting __name__ = '__main__' inside the package context.
Sign up to write and run code in this lesson.
Yesterday you split the inventory helpers into their own module and imported them from main.py. What happened the first time you ran the module file directly to test a function?
The whole thing ran. I had a test call at the bottom — print(normalize_name("WIDGET-A ")) — just to check the output. When I ran the module directly it printed. Fine. But then I imported the module from main.py and it printed again. Middle of the dashboard startup, out of nowhere: widget-a.
Right. And that's the exact problem if __name__ == '__main__': solves. Think about the warehouse's two visitor modes.
Two visitor modes?
A logistics partner arrives through the loading dock — they want to pull inventory, fill orders, nothing else. They don't need a tour. A retail buyer visits for a showroom walkthrough — sample tables out, products displayed, guided tour running. Same building, same inventory, completely different behavior based on who walked in.
And a Python file is the same building. When you run it directly, you're the retail buyer — the full experience fires. When you import it, you're the logistics partner — just pull the functions, don't set off the alarms.
That's exactly it. Python sets a special variable called __name__ every time it runs a file. When you execute a file directly, __name__ is the string '__main__'. When you import that same file as a module, __name__ is the module's filename without the .py:
# inventory_utils.py
def normalize_name(name: str) -> str:
return name.strip().lower()
def count_low_stock(products: list[dict], threshold: int = 10) -> int:
return sum(1 for p in products if p["stock"] < threshold)
if __name__ == '__main__':
# Only runs when you execute this file directly.
# Importing this module never touches this block.
print(normalize_name('WIDGET-A '))
print("Direct execution — dev/test mode")Python sets __name__ automatically? Before any of my code runs?
Automatic, every time, no exceptions. Python sets it before a single line of your code runs. Execute python inventory_utils.py — Python sets __name__ = '__main__' in that file's namespace. main.py does import inventory_utils — Python sets __name__ = 'inventory_utils' instead. The guard reads that variable and decides whether to run the block.
So the functions are always available on import — normalize_name, count_low_stock — because they're defined at the top level outside the guard. Only the code inside the guard is conditional.
Correct. Function definitions at the top level always execute on import — that's how Python registers them. The guard doesn't hide your functions from importers. It only gates code that should run in script mode: test calls, setup, CLI entry points.
The test print I had yesterday — I just needed to put it inside the guard. One line of protection, problem gone.
One line. But I want to show you the other direction — what a well-structured script looks like when you use the guard intentionally, not just defensively:
# run_report.py
import csv
from inventory_utils import normalize_name, count_low_stock
def load_products(filepath: str) -> list[dict]:
with open(filepath, newline='') as f:
reader = csv.DictReader(f)
return [
{**row, "name": normalize_name(row["name"]), "stock": int(row["stock"])}
for row in reader
]
def print_report(products: list[dict]) -> None:
low = count_low_stock(products)
print(f"Total products: {len(products)}")
print(f"Low stock alerts: {low}")
if __name__ == '__main__':
products = load_products("warehouse.csv")
print_report(products)The functions are importable — someone could do from run_report import load_products and reuse it. But the actual report only runs when you execute the file. That's the showroom vs. loading dock split, right there in the code.
And this pattern scales to every serious Python project. Command-line tools, Django management commands, data pipeline scripts — they all use this pattern. The functions are importable by other Python code; the CLI fires only when you run the script directly.
Why '__main__' specifically? Is that string reserved?
Python hardcodes that string as the sentinel for direct execution. The double underscores are a Python convention for names the interpreter manages: __name__, __file__, __doc__, __init__ — called "dunder" attributes. They signal: Python itself manages this, not you.
Dunder. That sounds like a hobbit name. But I've been writing __init__ in empty package directories without knowing why — now it makes sense. It's a Python-managed name.
You'll see __name__ in a few other contexts too, but the core mechanic is always the same: Python sets it, you read it, you branch on it. Now let's look at what your challenge today actually analyzes:
def analyze_script(lines: list[str]) -> dict:
has_main_guard = False
function_count = 0
import_count = 0
would_run_on_import = []
for line in lines:
stripped = line.strip()
if stripped == "if __name__ == '__main__':" or stripped == 'if __name__ == "__main__":':
has_main_guard = True
elif stripped.startswith("def "):
function_count += 1
elif stripped.startswith("import ") or stripped.startswith("from "):
import_count += 1
elif stripped and not stripped.startswith(("def ", "class ", "#", "if __")):
would_run_on_import.append(stripped)
return {
"has_main_guard": has_main_guard,
"function_count": function_count,
"import_count": import_count,
"would_run_on_import": would_run_on_import,
}Static analysis — reading the lines as text, not executing them. It checks whether the guard exists, counts functions, counts imports, and flags everything else at the top level as code that fires on import.
Simplified, but that's the real idea behind pylint, flake8, mypy. They parse your source file and reason about it without running it. You're doing the same thing in miniature.
The would_run_on_import list is the interesting one. That's the code that will surprise whoever imports this module — the stuff that probably belongs inside the guard but isn't there yet.
Did you go back and put the Monday report's test calls inside the guard after yesterday?
I did. Diane said startup was cleaner. That's the highest praise I've ever received from Diane.
Tomorrow: packages. You've been putting files in the same directory. What happens when your project grows and you need to organize files into subdirectories — and have Python treat those subdirectories as organized namespaces? You've already seen __init__.py. Tomorrow that becomes intentional.
So a package is a directory Python knows about, and __init__.py is what makes it a package instead of just a folder?
That's the shape of it. But __init__.py does more than exist — it controls what the package exposes when someone writes from inventory import normalize_name. Tomorrow you design the import interface for a package, not just a module.
Every time Python runs a file — whether executed directly or imported — it sets the __name__ variable in that file's namespace:
python my_script.py): __name__ == '__main__'import my_script): __name__ == 'my_script'The guard if __name__ == '__main__': makes a block of code conditional on direct execution. Code outside the guard runs on both import and direct execution.
# inventory_utils.py
def normalize_name(name: str) -> str:
return name.strip().lower()
if __name__ == '__main__':
# Only runs when you execute this file directly
print(normalize_name('WIDGET-A ')) # prints: widget-a
# Importing this file never reaches hereWhy the guard matters:
"Dunder" convention: Names surrounded by double underscores (__name__, __file__, __doc__, __init__) are managed by Python itself. You read them; you don't create them.
Pitfall 1: Missing the guard on modules with side effects. import my_module executes the file. Any top-level print, network call, database connection, or file open outside the guard fires immediately on import. Always put executable behavior inside if __name__ == '__main__':.
Pitfall 2: Putting functions inside the guard. Functions defined inside the guard are not importable — they're only created during direct execution. Top-level def blocks are always importable, regardless of the guard.
Pitfall 3: Confusing __name__ with the filename. On import, __name__ is the module name (no .py), not the file path. For a file at utils/inventory.py imported as utils.inventory, __name__ is 'utils.inventory', not 'inventory' or the full path.
The if __name__ == '__main__': pattern pairs naturally with a main() function:
def main() -> None:
products = load_products("warehouse.csv")
print_report(products)
if __name__ == '__main__':
main()This makes the entry point itself importable — useful for testing and for tools that call your main() programmatically. The __main__.py file in a package lets the package run as a script with python -m package_name, setting __name__ = '__main__' inside the package context.