1.4.5 The if __name__ == "__main__": conditional
1.4.5 The if __name__ == "__main__": conditional jmk649The __name__ variable
You may have seen this line of code in other scripts or from PyScripter's new Python file template and wonder what it is for.
def main():
pass
if __name__ == '__main__':
main()The conditional if __name__ == "__main__": plays a very important role when we are importing other functions as modules and is used to determine whether code should be executed or not. I don't expect you to fully understand this concept right away; the important thing here is to know that it exists and serves an important purpose.
A note on the terminology used in this section: When we say the script is "ran/run directly" or is "directly executed", we are referring to the starting script from which all other functionality and process begins. It is the script being run, started, passed to the interpreter, c:/path-to-env/python.exe "script.py", etc.,.
When a Python script is executed, the interpreter process sets a few special variables, and one of them is __name__. If the script is being ran directly, __name__ is set to "__main__". If the script is being imported as a module in another script, __name__ is set to the name of the script/module.
Why use if __name__ == "__main__":?
This construct allows you to include code that you want to run only when the script is executed directly, and not executed when the script is imported by another script as a module. It is especially useful for including script function test code, setting exclusive script function arguments for one-off executions, and ensuring multiprocessing works correctly.
Consider the following two scripts:
script_A.py
import sys
def greet():
print("Hello from script_A!")
def update(script_name):
print(f"updated: {script_name}")
if __name__ == "__main__":
print("Running script_A directly.")
greet()
update(__name__)
# Print the sys modules to view the set names
for module_name, module in sys.modules.items():
if hasattr(module, '__name__'): # Check if the module has a __name__ attribute
print(f"Script A Module Name: {module_name}, __name__: {module.__name__}")
script_B.py
import sys
import script_A
print("Importing script_A at the top level...")
script_A.greet()
if __name__ == "__main__":
print("Running script_B directly.")
script_A.update(f"from {__name__}")
# Print the script's sys modules to view the set names
for module_name, module in sys.modules.items():
if hasattr(module, '__name__'): # Check if the module has a __name__ attribute
print(f"Script B Module Name: {module_name}, __name__: {module.__name__}")
When script_A.py is executed directly, it produces the following output (Note that this is a shortened list, yours will be much longer):
Running script_A directly. Hello from script_A! updated: script_A ... Script A Module Name: abc, __name__: abc Script A Module Name: io, __name__: io Script A Module Name: __main__, __name__: __main__ Script A Module Name: _stat, __name__: _stat Script A Module Name: stat, __name__: stat ...
When script_B.py is executed directly, it imports script_A and sets its __name__ to its module name, producing this output:
Importing script_A... Hello from script_A! Running script_B directly. updated: __main__ ... Module Name: script_A, __name__: script_A Module Name: script_B, __name__: __main__ ...
This naming process by the interpreter at runtime creates the compartmentalization of the imported module's functions within the main script and is the underlying mechanism that maps the <module>.<function> syntax we can use. Functions defined in the main script do not need the <module>. prefix since the interpreter automatically maps them as __main__.<function>. This naming prevents confusion if both scripts have functions with the same name since the functions at runtime are now __main__.greet() and script_A.greet().
Multiprocessing Safety
Using the if __name__ == "__main__": block is critical when working with multiprocessing, which we will be discussing in more detail in a later section so do not stress too much about what the multiprocessing code is doing, but focus on where the code is within the script. Consider this script:
safe_multiprocessing.py
import multiprocessing
def worker_function(number):
print(f"Worker {number} is working.")
if __name__ == "__main__":
print("Starting multiprocessing safely.")
processes = []
for i in range(3):
process = multiprocessing.Process(target=worker_function, args=(i,))
processes.append(process)
process.start()
for process in processes:
process.join()
Running this script produces the following output, as the multiprocessing code executes safely within the __main__ block since code in this block is executed once:
Starting multiprocessing safely. Worker 0 is working. Worker 1 is working. Worker 2 is working.
If we wanted to use the safe_multiprocessing's worker_function method in another script, we would be able to do safely without executing the multiprocessing code block since the script's __name__ would be set to 'safe_multiprocessing' when imported into the other script. The imported script's (safe_multiprocessing in this case) if __name__ == '__main__': would equate to False and prevent code within it from executing. We will see another variant of using a function within this conditional block in section 1.6.5.1, when we convert our HiHo Cherry-O game into a multprocessing script.
Common Pitfalls
If you omit the if __name__ == "__main__": block in a multiprocessing script, you may encounter infinite recursion or a RuntimeError. For example:
import multiprocessing
top_level_variable = 2 # top-level code that sets each time the script is loaded.
def worker_function(number):
# protected code that will only execute when the function is called.
print(f"Worker {number} is working.")
# top-level code that will execute each time the script is loaded into the interpreter (not safe)
processes = []
for i in range(3):
process = multiprocessing.Process(target=worker_function, args=(i,))
processes.append(process)
process.start()
for process in processes:
process.join()
Try reading through this script as if you were the interpreter. Each time a Process loads worker_function in line 12, it will load the entire script (as __main__.worker_function) and will again execute the top-level code (lines 1 and 3), load the function on line 5, and execute the top-level code on line 10 and 11. When you reach line 12, you will start reading at the top when you encounter the target=worker_function, method call. Do you ever reach any line under the method call in line 12? Running this script will cause Python to repeatedly re-import the script and execute the unprotected top-level code each time it imports, leading to an infinite loop or crash since it can't get past spawning new Processes at the line 12. Hopefully this will show you what could happen if and when code isn't protected.
Even if the script is imported as a module, any top level code (code not within a function, classes, or conditional) will execute when it is imported in during execution. Using the example above, the top_level_variable on line 3 is set for each Process spawned, but the print(f"Worker {number} is working.") within the function worker_function(..) on line 7 will not because it is never called as written. Method calls at the top-level will get executed each time the script is read in as well so if we deliberately execute a call worker_function(1) at the top-level, above the line 12 (for i in range(3):), it will execute once for each Process spawned.
Something to watch out for is if you forget to comment out top-level test code in scripts designed to be imported as modules, it may create a logical error by changing variables and paths due to the cascading Method Resolution Order (MRO). Simply, the incoming variables passed by the main calling script may be re-set to the values assigned in the imported script. For example, adding top_level_variable = 5 right before the process = multiprocessing.Process(target=worker_function, args=(i,)) call on line 12, will get reset to 2 each time the script is loaded.
This can be avoided if you safeguard top-level code by using the if __name__ == "__main__" construct as shown above in safe_multiprocessing.py. Reading through this script as if you were the interpreter, the code within the conditional block is executed once since the spawned Process script's __name__ at this point will not be '__main__', and will not execute in the Process because if__name__ == "__main__" will be False.
Even if unintended execution doesn’t cause issues, not using __name__ == "__main__" makes the script harder to maintain in the sense of compartmentalization and a growing code base. A good coding principle to follow is to structure your scripts with reusable, callable code (functions, classes) at the top, with direct executable code guarded inside if __name__ == "__main__": for testing or setting variables if the script is executed directly. This may seem like a counterintuitive structure to the way Python reads in scripts (top-to-bottom), but the variables created in the if __name__ == "__main__": block are set at the global level and accessible to the functions and classes if the script is executed directly.
Key Takeaways
- The
if __name__ == "__main__":construct separates script functionality from module functionality. - It provides reusable code in functions and Classes, allowing for either standalone execution or as a module.
- It prevents unintended execution of code during import and ensures safe multiprocessing.