When Python encounters a function while executing your source code, it does not immediately run the function. Rather, it compiles the function into an executable code object, and waits until you actually call the function.
This means the only time Python checks that cough() is really defined, is when you call main(). And since Python does find a cough function when main is called, it does not raise an error.
In other-words: Python does not verify the names used in function actually exist until run-time, so you so allowed to use currently undefined variable names.
This is the same reason a function such as this doesn't raise an error when defined, but it does during run-time:
>>> def func():
a + b
>>> func # func was compiled...
<function func at 0x7f8ddd5d6488>
>>> func() # but we cannot call it.
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
func() # but we cannot call it.
File "<pyshell#7>", line 2, in func
a + b
NameError: name 'a' is not defined
>>>
Also note that if you try to call main before cough has been defined, you will get an error:
>>> def main():
for i in range(3):
cough()
>>> main()
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
main()
File "<pyshell#12>", line 3, in main
cough()
NameError: name 'cough' is not defined
>>>
This shows that Python relies on every name in your function to have already been defined whether globally or locally, before you attempt to use them.