What Does if __name__ == “__main__”: do; Python’s __main__ Scope

Jonathan Turnock
4 min readAug 31, 2019

--

Photo by Max Nelson on Unsplash

If you have spent any amount of time with the Python programming language, you might have come across the main scope conditional. It looks something like this:

if __name__ == "__main__":
...

What this method does is perfectly summarised in the Python Docs for the standard library:

A module can discover whether or not it is running in the main scope by checking its own __name__, which allows a common idiom for conditionally executing code in a module when it is run as a script or with python -m but not when it is imported

(Docs.python.org, 2019)

Let’s take a look at some examples, why we might find it and under what conditions it is triggered.

Module Design Patterns

Before we dive into the examples, let’s take a quick look at Pythons Module design patterns. It will give us some background as not all python modules are equal.

At the broad level, we have two main types of modules:

  1. Runnable Scripts
    Modules designed as scripts are Procedural. They may leverage Objects to get work done, indeed in Python, they most definitely do. However, the takeaway here is that you cannot import a module written as a Procedural script because the business logic is executed during the import. If you happen to have a re-usable component in that module, you cannot import that file because it will cause the code to execute.
  2. Importable Modules
    Modules designed for re-use, they contain re-usable software components such as classes. Importable modules are a staple part of Object-Oriented Programming. In Python, some modules are designed to be imported as a whole, others more suited to importing specific functions/classes.
    Examples include modules like configparser, logging and time.

Naturally, you can see how there is a benefit in making a module both Runnable and Importable. To get the maximum utility out of our code written, we should make it Importable and second, make it Runnable.

If you are using unit testing, which any code should do, then you will find that approaching python modules in this way significantly increases the testability of the code because it should be imported by the test and then run by the end-users.

Runnable Example

We can illustrate a runnable example in this short tax calculating script; we are tasked with creating an application that asks the user for the transaction total. It adds 20% to that total and prints out the billable amount:

transaction_value = float(input("What was the sale value?\n$:"))
tax_rate = 0.20

tax_amount = transaction_value * tax_rate

print(f'Billable: ${transaction_value + tax_amount}')

Result:
What was the sale value?
$:5.0
Billable: $6.0

Functionally there is nothing wrong with this application. It works just fine as it is. However, there will be changes needed. We have our business logic (Calculating Tax) embedded within the script.

You will also notice that there is no way to test the tax calculation with a range of values automatically.

Further to this, if we wanted to add some auditing, we would have to change this script. Potentially any other scripts using similar logic to perform this new auditing would also need changes. Things quickly get out of hand as business requirements evolve.

Reusable Software Components

To solve the challenge highlighted above, we introduce a re-usable software component, the TaxCalculator class.

class TaxCalculator:
def __init__(self, tax_rate = 0.20):
self.tax_rate = tax_rate

def calculate_tax(self, transaction_value):
tax_amount = transaction_value * self.tax_rate
self.__audit(self.tax_rate, transaction_value, tax_amount)
return tax_amount + transaction_value

def __audit(self, tax_rate, transaction_value, tax_amount):
print(f"AUDIT_EVENT:TR_{tax_rate}:TV_{transaction_value}:TA_{tax_amount}")

tax_calculator = TaxCalculator()
transaction_value = float(input("What was the sale value?\n$:"))
print(f'Billable: ${tax_calculator.calculate_tax(transaction_value)}')

Result:
What was the sale value?
$:5.0
AUDIT_EVENT:TR_0.2:TV_5.0:TA_1.0
Billable: $6.0

We have now de-coupled our business logic away from our actual application, hurrah time for a coffee ☕️ .

But hold your 🐴 , we have not made any difference to the re-usability of our module! If someone wanted to import it, they could not do so. The code for our command-line app will still run when the module is imported as everything with leftmost indentation will run on import.

__main__ — Top-level script environment

To combat this, Python has a name it assigns to the top-level script environment, “__main__”.

The rules are simple:

  1. If you run any python module its special variable __name__ is set to __main__ indicating it is the top-level script environment.
  2. If you import any python module, that name becomes the name of the module.

Try it out!

Add the following to a module:

print(f"__name__ is set to '{__name__}'")

When run we get __name__ is set to '__main__'

When imported we get __name__ is set to 'example_module'

Importable and Runnable

You should see by now that by checking the __name__ special variable at runtime, we can effectively introspect how the module was invoked. We know that if __name__ is set to __main__ It is the top-level script environment. Otherwise, it has been imported.

Using this knowledge, we can introduce a conditional statement that causes our script to run if the module is the top-level script environment. Otherwise, don’t run and allow the module to be imported without any of the script behavior. This way, we have made maximum use of the code, and it’s now both runnable and importable.

class TaxCalculator:
...

if __name__=="__main__":
tax_calculator = TaxCalculator()
transaction_value = float(input("What was the sale value?\n$:"))
...

Conclusion

In this article, we covered off how Python populates the __name__ special variable, we learned that if you run a module __name__ is set to __main__ indicating it is the top-level script environment.

We learned how writing pure unconditional scripts stop a python module from being Importable. If we have any reusable and testable software components within a module, it must be importable as a priority and using the conditional check if __main__=="__main__": allows us to make a single module, both importable and runnable.

Docs.python.org. (2019). __main__ — Top-level script environment — Python 3.7.4 documentation. [online] Available at: https://docs.python.org/3/library/__main__.html [Accessed 31 Aug. 2019].

--

--