3

I am working on a project where I have three python modules (a.py, b.py and c.py).

Module a is calling module b, module b is calling module c, and module c is calling module a. But the behaviour is very bizzare when it runs.

Here are my three modules:

a.py

print('module a')
def a() : 
    print('inside a')
    return True

import b
b.b()

b.py

print('module b')

def b() : 
    print('inside b')
    return True

import c

c.c()

c.py

print('module c')

def c() : 
    print('inside c')
    return True

import a

a.a()

When I run a.py, the output observed is :

module a
module b
module c
module a
inside b
inside a
inside c
inside b

Whereas the expected behavior is:

module a
module b
module c
module a
inside b

Why does this happen? Is there an alternative way for such an implementation?

ironstein
  • 421
  • 5
  • 16
  • Can you give a specific example of "bizzare" behaviour? – Gilbert Allen Jul 14 '15 at 11:57
  • Are you saying it's bizarre because of the circular importing or is it actually causing erratic behaviour when you run it? Can you show your code? – SuperBiasedMan Jul 14 '15 at 12:02
  • Bizarre as in the modules run as soon as imported, and then every time you call their main function? That would be my first guess. Please define the behavior. – maccartm Jul 14 '15 at 12:04
  • I posted an answer explaining why you got that output. I may be able to help you understand how to do what you want, but at the moment your problem is abstract and unclear. Why do you want only `a` to run but you want the others imported? What's the use case? – SuperBiasedMan Jul 14 '15 at 12:24
  • @SuperBiasedMan I want my module a to be able to communicate with module c via b at the same time allowing module c to call functions or exceptions in module a – ironstein Jul 14 '15 at 12:35
  • But why do b and c need to have loose code in them that's not wrapped in a function? – SuperBiasedMan Jul 14 '15 at 13:03

2 Answers2

2

This has to do with stack frames and how functions and imports are called.

You start by running a.py.

'module a'

First thing that happens: import b:

'module b'

Within b, c.py is imported:

'module c'

Module c imports a, and we get:

'module a'

b has already been imported from running a.py in the first place, so this call of import b is passed (we do not re-import b.py). We then see the effects of calling b.b(), the next statement:

inside b

And we return to c.py's frame, where we call a.a():

inside a

c.py has run its course; next we jump back to b.py, where we left off (right after importing), and call c.c():

inside c

Now b.py has finished running as well. Finally we return to the a.py frame from which we ran the program, and call b.b():

inside b

Hope this helps explain this behavior. Here's an example of how you could rectify this problem and get your desired output:

a.py:

print("module a")
import b

def main():
    b.b()

def a():
    print("inside a")

if __name__ == "__main__":
    main()

b.py:

print("module b")
import c

def main():
    c.c()

def b():
    print("inside b")

if __name__ == "__main__":
    main()

c.py:

print("module c")
import a

def main():
    a.a()

def c():
    print("inside c")

if __name__ == "__main__":
    main()

Here's a link to explain what happens with the if __name__ == "__main__": main() call. Essentially it will only run the main() function for each script if that is the script that is built & run in the first place. In this way, you get the desired output:

module a
module b
module c
module a
inside b
Community
  • 1
  • 1
maccartm
  • 2,035
  • 14
  • 23
  • why does module c import a again, when we had a import b and b import c in the first place – ironstein Jul 14 '15 at 12:37
  • **a.py** has never been imported, it simply started running the script; **c.py** has every right to `import a` and run the things that happen within **a.py** once again. It is only once we reach `import b` for the second time that we have imported everything and we can essentially "skip" the imports (they aren't actually skipped, you get a reference but their [code doesn't run](http://stackoverflow.com/questions/19077381/what-happens-when-i-import-module-twice-in-python)). Take a look at my edit to see how you would probably expect it to run. – maccartm Jul 14 '15 at 12:42
  • 1
    If you incorporate the `if __name__ == "__main__":` you can ensure that the other scripts (b & c) only run when you want them to: when you call `b.main()` and `c.main()`. Importing, however, will run everything not contained within a function. – maccartm Jul 14 '15 at 12:45
  • Wow, thanks that helped. I have one more question- I was under the impression that when module c calls module a, it will be calling an instance of module a. So it should not have the __name__ as __main__. Where am I wrong – ironstein Jul 14 '15 at 12:56
  • If this solution helped you consider [accepting the answer](http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work) to let others know the problem has been solved. Glad I could help! – maccartm Jul 14 '15 at 12:58
  • I basically want to be able to call functions of each of the modules from each of the modules and return to the previous module once the task is completed – ironstein Jul 14 '15 at 13:00
  • 1
    If you wrap everything that you want to call within functions as shown you should be able to accomplish this; code will only execute when you call `someModule.main()`. Try implementing the code how I have shown at the bottom of my answer and see what I mean by adding in calls to `b.main()` and `c.main()`. – maccartm Jul 14 '15 at 13:03
1

I think the key misunderstanding is that you don't expect all the modules to run after their imports, but they do. They get interrupted mid script to do another import but they will return to finish out the commands.

So what ends up happening is: (I'm removing the function declarations, just for clarity)

print('module a')
import b
>>> importing b
    print('module b')
    import c
    >>> importing c
        print('module c')
        import a
        >>> importing a
            print('module a')
            import b
            >>> Doesn't reimport b
            b.b()
        a.a()
    c.c()
b.b()

So to just show the order of commands without the imports and nesting:

print('module a')
print('module b')
print('module c')
print('module a')
b.b()
a.a()
c.c()
b.b()

And this does match your actual output.

SuperBiasedMan
  • 9,814
  • 10
  • 45
  • 73