Clarify ifmain with better, simpler, A.py B.py examples.
This commit is contained in:
parent
d900a927e3
commit
8efe85d45f
1 changed files with 176 additions and 65 deletions
|
@ -3,99 +3,210 @@
|
||||||
Python's `if __name__ == '__main__'`
|
Python's `if __name__ == '__main__'`
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
|
When you read other people's Python source code, you may find something like this at the very bottom of the file:
|
||||||
|
|
||||||
|
```Python
|
||||||
|
if __name__ == '__main__':
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
This is a construct that is confusing the first time you encounter it. `if __name__ == '__main__'` is used to make a Python file that is importable by other Python files, as well as executable on its own.
|
This is a construct that is confusing the first time you encounter it. `if __name__ == '__main__'` is used to make a Python file that is importable by other Python files, as well as executable on its own.
|
||||||
|
|
||||||
## Understanding if name == main
|
## Understanding if name == main
|
||||||
|
|
||||||
In order to understand how it works and how to use it, there are a few background pieces of information you need to know.
|
In order to understand how it works and how to use it, there are a few background pieces of information you need to know.
|
||||||
|
|
||||||
1. When you are writing a program `A.py` that imports another module `B.py`, the code inside `B.py` is executed just like if you ran `B.py` by itself.
|
### Part 1
|
||||||
|
|
||||||
I know that sounds simple, but it's something people don't usually think about when practicing imports for the first time. Here is an example:
|
When you are writing a program `A.py` that imports another module `B.py`, the code inside `B.py` is executed just like if you ran `B.py` by itself.
|
||||||
|
|
||||||
```Python
|
I know that sounds simple, but it's something people don't usually think about when practicing imports for the first time. Here, watch:
|
||||||
# hextools.py
|
|
||||||
'''
|
```Python
|
||||||
This library provides functions for converting to and from hex.
|
# B.py
|
||||||
'''
|
print("I'm the B file!")
|
||||||
def int_to_hex(x):
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
>python B.py
|
||||||
|
I'm the B file!
|
||||||
|
```
|
||||||
|
|
||||||
|
```Python
|
||||||
|
# A.py
|
||||||
|
print("I'm the A file, and I'm going to import B!")
|
||||||
|
import B
|
||||||
|
print("I'm all done importing B!")
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
>python A.py
|
||||||
|
I'm the A file, and I'm going to import B!
|
||||||
|
I'm the B file!
|
||||||
|
I'm all done importing B!
|
||||||
|
```
|
||||||
|
|
||||||
|
Suppose you write a file called `hextools.py` so that you can practice converting between hex and ints.
|
||||||
|
|
||||||
|
```Python
|
||||||
|
# hextools.py
|
||||||
|
'''
|
||||||
|
This library provides functions for converting to and from hex.
|
||||||
|
'''
|
||||||
|
def int_to_hex(x):
|
||||||
'''
|
'''
|
||||||
Python automatically starts it with 0x which we'll strip.
|
Python automatically starts it with 0x which we'll strip.
|
||||||
'''
|
'''
|
||||||
return hex(x)[2:]
|
return hex(x)[2:]
|
||||||
|
|
||||||
def hex_to_int(h):
|
def hex_to_int(h):
|
||||||
return int(h, 16)
|
return int(h, 16)
|
||||||
|
|
||||||
print(int_to_hex(4000))
|
print(int_to_hex(4000))
|
||||||
```
|
```
|
||||||
|
|
||||||
```Python
|
```
|
||||||
# myprogram.py
|
>python hextools.py
|
||||||
import hextools
|
fa0
|
||||||
|
```
|
||||||
|
|
||||||
x = int(input('Please type a number: '))
|
It works great.
|
||||||
print(x, 'in hex is', hextools.int_to_hex(x))
|
|
||||||
```
|
|
||||||
|
|
||||||
Now let's run that program.
|
Then one day, you need to write a second program that also does some hex-int conversion. Well, instead of copying and pasting those functions, it makes sense to just import hextools.
|
||||||
|
|
||||||
```
|
```Python
|
||||||
>python myprogram.py
|
# myprogram.py
|
||||||
fa0
|
import hextools
|
||||||
Please type a number:
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice that the print statement inside `hextools` is executed, and shows fa0 before `myprogram` even asks for input. Well, that's because all the code inside hextools is executed simply by importing it. When I say "all the code", I'm not saying that it calls the functions defined within that file, but I mean the `def` statements which make those functions exist in the first place and, clearly, anything else on the global scope.
|
x = int(input('Please type a number: '))
|
||||||
|
print(x, 'in hex is', hextools.int_to_hex(x))
|
||||||
|
```
|
||||||
|
|
||||||
2. Python modules have some special variables in the global scope that are created automatically.
|
Now let's run that program.
|
||||||
|
|
||||||
Try starting a python interpreter and calling `dir`.
|
```
|
||||||
|
>python myprogram.py
|
||||||
|
fa0
|
||||||
|
Please type a number:
|
||||||
|
```
|
||||||
|
|
||||||
```
|
That's annoying -- the print statement inside `hextools` is executed, and shows fa0 before `myprogram` even asks for input. Well, that's because all the code inside hextools is executed simply by importing it. When I say "all the code", I'm referring to anything that's written on the global scope -- anything which touches the left edge of the page. That means the `def` statements which create the functions and, in this case, the print statement that we left there.
|
||||||
>python
|
|
||||||
>>> dir()
|
|
||||||
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
|
|
||||||
'__package__', '__spec__']
|
|
||||||
>>> __name__
|
|
||||||
'__main__'
|
|
||||||
```
|
|
||||||
|
|
||||||
Ok so `__name__` is `'__main__'` but let's keep going.
|
Now you know that importing a file causes that code to run. Hold that thought, it's time for the second piece of background information.
|
||||||
|
|
||||||
Let's start an interpreter with the `-i` flag which runs everything in the file and then gives you the repl afterward.
|
### Part 2
|
||||||
|
|
||||||
```
|
Python modules have some special variables in the global scope that are created automatically.
|
||||||
>python -i hextools.py
|
|
||||||
fa0
|
|
||||||
>>> dir()
|
|
||||||
['__annotations__', '__builtins__', '__cached__', '__doc__', '__loader__',
|
|
||||||
'__name__', '__package__', '__spec__', 'hex_to_int', 'int_to_hex']
|
|
||||||
>>> __doc__
|
|
||||||
'\nThis library provides functions for converting to and from hex.\n'
|
|
||||||
>>> __name__
|
|
||||||
'__main__'
|
|
||||||
```
|
|
||||||
|
|
||||||
Well, there's that fa0 again. As you can see, there is a magical variable called `__doc__` which automatically took the docstring that I wrote at the top of the file. I'm showing that to prove that this is exactly what the variables look like while Python is executing this file. And we see the variable `__name__` which again says `'__main__'`.
|
Try starting a python interpreter and calling `dir`.
|
||||||
|
|
||||||
One more demonstration. Let's check that name variable again, but with an imported module:
|
```
|
||||||
|
>python
|
||||||
|
>>> dir()
|
||||||
|
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
|
||||||
|
'__package__', '__spec__']
|
||||||
|
>>> __name__
|
||||||
|
'__main__'
|
||||||
|
```
|
||||||
|
|
||||||
```
|
Ok so there's an automatic variable called `__name__` which is a string that says `'__main__'` but let's keep going to figure out what it means.
|
||||||
>python
|
|
||||||
>>> import hextools
|
|
||||||
fa0
|
|
||||||
>>> __name__
|
|
||||||
'__main__'
|
|
||||||
>>> hextools.__doc__
|
|
||||||
'\nThis library provides functions for converting to and from hex.\n'
|
|
||||||
>>> hextools.__name__
|
|
||||||
'hextools'
|
|
||||||
```
|
|
||||||
|
|
||||||
Aha. So the `__name__` of my Python repl is `'__main__'`, but the `__name__` of an imported module is it's name.
|
Let's start an interpreter with the `-i` flag which runs everything in the file and then gives you the repl afterward.
|
||||||
|
|
||||||
Now let's combine these two lessons together. Any code that you write on the global scope will be executed when you import that file into another. But, the magic variable `__name__` will **only** say `'__main__'` when you are running that file directly, otherwise it will just say the name of the module.
|
```
|
||||||
|
>python -i hextools.py
|
||||||
|
fa0
|
||||||
|
>>> dir()
|
||||||
|
['__annotations__', '__builtins__', '__cached__', '__doc__', '__loader__',
|
||||||
|
'__name__', '__package__', '__spec__', 'hex_to_int', 'int_to_hex']
|
||||||
|
>>> __doc__
|
||||||
|
'\nThis library provides functions for converting to and from hex.\n'
|
||||||
|
>>> __name__
|
||||||
|
'__main__'
|
||||||
|
```
|
||||||
|
|
||||||
|
Well, there's that fa0 again. As you can see, there is a magical variable called `__doc__` which automatically took the docstring that I wrote at the top of the file. I'm showing that to prove that this is exactly what the variables look like while Python is executing this file. And we see the variable `__name__` which again says `'__main__'`.
|
||||||
|
|
||||||
|
One more demonstration. Let's check that name variable again, but with an imported module:
|
||||||
|
|
||||||
|
```
|
||||||
|
>python
|
||||||
|
>>> import hextools
|
||||||
|
fa0
|
||||||
|
>>> __name__
|
||||||
|
'__main__'
|
||||||
|
>>> hextools.__doc__
|
||||||
|
'\nThis library provides functions for converting to and from hex.\n'
|
||||||
|
>>> hextools.__name__
|
||||||
|
'hextools'
|
||||||
|
```
|
||||||
|
|
||||||
|
Aha. So the `__name__` of my Python repl is `'__main__'`, but the `__name__` of an imported module is it's name.
|
||||||
|
|
||||||
|
```
|
||||||
|
>>> import os
|
||||||
|
>>> os.__name__
|
||||||
|
'os'
|
||||||
|
>>> import sys
|
||||||
|
>>> sys.__name__
|
||||||
|
'sys'
|
||||||
|
>>> import requests
|
||||||
|
>>> requests.__name__
|
||||||
|
'requests'
|
||||||
|
>>>
|
||||||
|
>>> __name__
|
||||||
|
'__main__'
|
||||||
|
```
|
||||||
|
|
||||||
|
There is only one module that can be `'__main__'`, and that's whatever file we run directly. Anything you import will just have its own name.
|
||||||
|
|
||||||
|
|
||||||
|
### Part 3
|
||||||
|
|
||||||
|
Let's combine these two lessons together. Any code that you write on the global scope will be executed when you import that file into another. But, the magic variable `__name__` will **only** say `'__main__'` when you are running that file directly, otherwise it will just say the name of the module.
|
||||||
|
|
||||||
|
```Python
|
||||||
|
# B.py
|
||||||
|
print("I'm the B file, and my name is", __name__)
|
||||||
|
```
|
||||||
|
|
||||||
|
```Python
|
||||||
|
# A.py
|
||||||
|
print("I'm the A file, and I'm going to import B!")
|
||||||
|
import B
|
||||||
|
print("I'm all done importing B!")
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
>python B.py
|
||||||
|
I'm the B file, and my name is __main__
|
||||||
|
|
||||||
|
>python A.py
|
||||||
|
I'm the A file, and I'm going to import B!
|
||||||
|
I'm the B file, and my name is B
|
||||||
|
I'm all done importing B!
|
||||||
|
```
|
||||||
|
|
||||||
|
This should drive the point home:
|
||||||
|
|
||||||
|
```Python
|
||||||
|
# B.py
|
||||||
|
print("I'm the B file, and my name is", __name__)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print("The B file is being run directly!")
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
>python B.py
|
||||||
|
I'm the B file, and my name is __main__
|
||||||
|
The B file is being run directly!
|
||||||
|
|
||||||
|
>python A.py
|
||||||
|
I'm the A file, and I'm going to import B!
|
||||||
|
I'm the B file, and my name is B
|
||||||
|
I'm all done importing B!
|
||||||
|
```
|
||||||
|
|
||||||
Therefore, by putting code inside an `if __name__ == '__main__'`, it will only run when you are running that file directly, and it will be skipped if the module is being imported by something else. There's nothing surprising about how this works. It's just an if statement!
|
Therefore, by putting code inside an `if __name__ == '__main__'`, it will only run when you are running that file directly, and it will be skipped if the module is being imported by something else. There's nothing surprising about how this works. It's just an if statement!
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue