1

I am trying to understanding cyclic import problem, Here I have three python files,

py1.py

import py3
py3.test3()
def test1():
    print 'test 1'

py2.py

import py1
py1.test1()
def test2():
    print 'test 2'

py3.py

import py2
py2.test2()
def test3():
    print 'test 2'

when I run the python py3.py and got the error like this,

Traceback (most recent call last):
  File "py3.py", line 1, in <module>
    import py2
  File "/home/me/Desktop/hackerearth/cylic/py2.py", line 1, in <module>
    import py1
  File "/home/me/Desktop/hackerearth/cylic/py1.py", line 1, in <module>
    import py3
  File "/home/me/Desktop/hackerearth/cylic/py3.py", line 3, in <module>
    py2.test2()
AttributeError: 'module' object has no attribute 'test2'

But when I remove the import py3 from py1.py file I got the output without any errors. Any one explain me why I am got this error.

dhana
  • 6,487
  • 4
  • 40
  • 63

1 Answers1

8

In Python, files are executed as they are parsed. A function doesn't exist until its def has "executed".

Your problem is this: When you import a module, the parser stops where it's at in that file, and begins parsing (executing) the new file being imported.

So, when you run python py3.py, it begins parsing the file. It gets to import py2, then stops what it's doing to go start parsing py2.py, and so forth for py1.py.

When it gets to import py3, it again starts paring it. However, when it gets to import py2, it realizes it has already imported py2, and goes to the next line. Here, it encounters py2.test2(). However, remember that we never finished parsing py2, so test2() does not exist yet.

In order of execution:

# start at py3.py
import py2

# now in py2.py
import py1

# now in py1.py
import py3

# now in py3.py - this time being parsed as module
import py2       # this already happened, skip it.
py2.test2()      # doesn't exist yet

If you add print statements to the very top of your files, you will see this behavior.


The real problem is that you have global executable statements. If, instead, you include the typical construct:

if __name__ == '__main__':
    # First real executable statement goes here

Then you'll never try to "do anything" before a module has fully imported.

Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328