1

This code was picked up from an answer in this thread What does if __name__ == "__main__": do?

# Suppose this is foo3.py.

def functionA():
    print("a1")
    from foo3 import functionB
    print("a2")
    functionB()
    print("a3")

def functionB():
    print("b")

print("t1")
print("m1")
functionA()
print("m2")
print("t2")

I think the code is executed as follows (when executed as main python3 foo3.py):
1. Prints t1
2. Prints m1
3. Enters functionA and prints a1
4. Imports functionB from foo3, thus running foo3 again. Goes back to step 1
Can you help me correct my analysis?

1 Answers1

2

It does not "run foo3 again", it runs the foo3.py script again. The first time foo3.py was running was to produce the module __main__, the second time to produce the module foo3.py.

The behaviour is in fact (almost) as if you had a file named __main__.py and another named foo3.py both with these exact same content and you then run python __main__.py. This is what is happening.

Only, Python fakes it so that it looks as if the program started from a script named __main__.py no matter what the actual Python file was. The only telltale sign to contrary is that __file__ would tell the filename of the actual script, i.e. /spam/ham/eggs/foo3.py.


The reason why it does not go to an infinite loop is that import looks for a module with the given name in sys.modules - if it is already present there it does not execute any new files. Upon startup Python will create an entry for __main__ in sys.modules, and the code of the startup script (foo3.py) is executed within the scope of this module.

Then when it executes the statement import foo3 it will check if foo3 has an entry in sys.modules. As it is not there, a new empty module named foo3 is created, placed into sys.modules, and the code of foo3.py is executed within the scope of this new empty module.

It eventually executes the import 2nd time. This time there is foo3 in sys.modules, so importing does not create or load any more scripts, just returns the already-loaded module.

To get the "infinite" loop you can delete the already-imported module reference from sys.module prior to importing foo3 again:

import sys

def functionA():
    print("a1")

    if 'foo3' in sys.modules:
        del sys.modules['foo3']

    from foo3 import functionB
    print("a2")
    functionB()
    print("a3")

def functionB():
    print("b")

print("t1")
print("m1")
functionA()
print("m2")
print("t2")

And when run you'll get

  [....]
  File ".../foo3.py", line 7, in functionA
    from foo3 import functionB
  File ".../foo3.py", line 17, in <module>
    functionA()
  File ".../foo3.py", line 7, in functionA
    from foo3 import functionB
RuntimeError: maximum recursion depth exceeded while calling a Python object
  • Thanks for the clear explanation! If I want to learn more about how python works and get a good grasp over the language, do you suggest any good resources? – Prashanth Ravichandar Aug 17 '19 at 06:56
  • @PrashanthRavichandar reading the source code would be a good one - although it might be a bit similar to jumping into a river without any gear and trying to learn to swim. Just my two cents. – Wiggy A. Aug 17 '19 at 07:06
  • 1
    Reading source code will lead to cargo culting, i.e. you're repeating the patterns you're seeing in other people's code without due understanding. For Python, the **official** Python tutorial, PEPs and various books are good. – Antti Haapala -- Слава Україні Aug 17 '19 at 07:07