Three python modules, calling one another - python

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?

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

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.

Related

Organizing functions into modules based on choice

I have 2 Python modules (e.g. A.py & B.py) that have the same function names (the implementation is different though). A third module (C.py) has a function that requires the functions in A or B, depending on user choice.
A fourth module (D.py) will import either A or B, and then C.
How do I correctly setup the modules and imports?
# module A.py
def write():
print('A')
# module B.py
def write():
print('B')
# module C.py
def foo():
write()
# module D.py (main executed module)
if choiceA:
import A
import C
else:
import B
import C
C.foo()
This is essentially a basic case of a Strategy Pattern. Instead of doing a double-import and implicitly expecting module C to get the right module, you should just explicitly pass the appropriate selection for it to call.
Using modules A and B as before:
# module C.py
def foo(writer):
writer.write()
# module D.py
import A
import B
import C
if choiceA:
my_writer = A
elif choiceB:
my_writer = B
C.foo(my_writer)
This will also continue to work exactly the same way if you choose to define A, B, and C as a class hierarchy instead of modules.
Not much really.
Module A.py:
def write():
print('A')
Module B.py
def write():
print('B')
Module C.py
def foo(choice):
if choice == 'A':
import A
A.write()
elif choice == 'b':
import B
B.write()
else:
#whatever
module D.py
choice = input('Enter choice: ')
import C
C.foo(choice)

Subclass __init__ not seeing superclass conditonally-imported module

I have a conditional import in a self-initialized instance of a superclass, but subclass cannot see the module (python 2.7):
class A(object):
def __init__(self, arg1):
self.attr1 = self.method1(arg1)
def method1(self, arg1):
if arg1 == 'foo':
import amodule
return amodule.method1()
else:
return 'not a dependency on foo'
class B(A):
def __init__(self, arg1):
super(B, self).__init__(arg1)
if arg1 == 'foo':
self.attr2 = self.method2(self.attr1)
def method2(self, attr1):
return amodule.method2()
if __name__=='__main__':
b = B("foo")
print b.attr2
This throws NameError: global name 'amodule' is not defined. a = A("foo") works just fine
Shouldn't the super call have executed import amodule in this case? (And using import should have put the module into globals?)
Doesn't import add /amodule/ to the global namespace of the currently
executing module? (__main__)?
No, the module is added to sys.modules but if it was imported locally then you won't have any references to it anymore. i.e the name amodule is now gone.
You could still access the module using sys.modules:
def method2(self, attr1):
import sys
return sys.modules['amodule'].method2()
Or you could import it using import amodule again and it will be picked up from sys.modules.
# Here b.py contains
# print('Module b was imported')
def func1():
print('inside func1')
import b
def func2():
print('inside func2')
import sys
print(sys.modules['b'])
import b
def func3():
print('inside func3')
import b
import sys
print('Deleted b')
del sys.modules['b']
import b
func1()
print()
func2()
print()
func3()
Demo:
inside func1
Module b was imported
inside func2
<module 'b' from '/Users/ashwini/py/b.py'>
inside func3
Deleted b
Module b was imported
Try putting import amodule on the first line of the program.
The reason is that amodule is imported in method1, method2 does not have access.
If you follow your code, you would see you don't reach method1().
When you create the object
b = B(foo)
You go through A.init() becuase the call for super(). However, your init of class A() doesn't include any import statements. Then the A.__init__ part is done, and you continue with B.__init__(). The next command is a call to amodule object, which wasn't imported at all.
You can add an helper method, which checks if the arg equals 'Foo' and if so import the module. Then add a call to this function in A.__init__() function.
On another note, __init__() job is to initialize variables. It shouldn't return anything.

Can I use module name as a 'variable' in Python?

Is it possible to use the name of the module as a variable to send to a function and specify/restrict some object with it? Thanks!
module1.py
def foo(a, b):
return (a + b) / 2.0
module2.py
def foo(a, b):
return 2.0 * a*b / (a+b)
file3.py
import module1
import module2
def do(a, b, module_name):
return module_name.foo(a, b)
Following your question, I assumed that you want to be able to invoke the foo method of any locally imported modules, providing args + module_name (as a string, like the variable name suggests).
Using locals() instead of sys.modules narrows the search to the modules imported in this file.
My solution:
import inspect
import module1
import module2
def do(a, b, module_name):
module = locals().get(module_name)
if inspect.ismodule(module):
return module.foo(a, b)
raise NameError('no such module imported with this name')
You can access modules in the form of a dictionary for easy lookup using sys.modules:
import module1
import module2
import sys
def do(a, b, module_name):
return sys.modules[module_name].foo(a, b)
This way they can be called for example:
do(3, 5, 'module1') # returns 4.0
do(3, 5, 'module2') # returns 3.75

Check if class B is subclass of class A which is located in the same module

With the following setup of the two files a.py
#File a.py
import imp
import inspect
class A(object):
pass
if __name__ == "__main__":
mod = imp.load_source("B", "b.py")
for _, c in inspect.getmembers(mod, inspect.isclass):
print issubclass(c, A)
and
#b.py
from a import A
class B(A):
pass
How do I check in file a.py if a class found in b.py is a subclass of A.
The attempt you see in a.py results in two False being printed. Since B is a subclass of A how do I check it crrectly?
I have found the following solution:
#File a.py
import imp
import inspect
class A(object):
pass
if __name__ == "__main__":
mod = imp.load_source("B", "b.py")
#self import
import a
for _, c in inspect.getmembers(mod, inspect.isclass):
print issubclass(c, a.A)
but still I don't have any idea why it works (while your solution doesn't)

Sharing Global variables in python

I have 2 files a.py and b.py as follows:
a.py
import b.py
Test="abc"
def main(args):
global Test
if args.target=="this":
Test="klm"
b.fun()
#rest of the body which I intend to execute only once
#hence I cannot call main() again
if __name__ == "__main__":
#some arguments are parsed
args = parser.parse_args()
main(args)
b.py
import a
print a.Test
EDIT: Output:
python a.py
abc
So basically my question is why is the Test variable not getting updated in b.py and how can I make this work? Thanks.
import a
a.main()
print a.Test
a.Test = "new Value"
print a.Text
You never invoke the main function. When you import a module, __name__ is not "__main__", so your main() never runs. When you run a.py directly it will run main()
Added due to question edit:
You need to consider the ordering of the imports execution. Consider these working files.
a.py
print("Entering a")
import argparse
import b
Test="abc"
print("Id of Test: ", id(Test))
def main(args):
global Test
if args.target=="this":
Test="klm"
b.fun()
#rest of the body which I intend to execute only once
#hence I cannot call main() again
if __name__ == "__main__":
#some arguments are parsed
print('Entering main')
parser = argparse.ArgumentParser()
parser.add_argument('--target', dest='target', type=str)
args = parser.parse_args()
main(args)
b.py
print("Entering b")
import a
print a.Test
def fun():
pass
The console produces the following:
$ python a.py
Entering a
Entering b
Entering a
('Id of Test: ', 40012016L)
abc
('Id of Test: ', 40012016L)
Entering main
The problem is, when you import a python module/file, you will immediately execute all the statements in that module. As such, you have a problem with your dependencies (aka imports), because b is importing a before the value of Test is 'corrected' and then immediately acting on this.
Consider two changes. First, introduce a third file config.py that contains this configuration information and that b does not import a. Second, move all your statements that require this config in b into functions that are called/bootstrapped by a, as obviously intended.
Previous answer:
I have a solution demonstrating the issue, by only modifying b.py
def fun(): # Added because your main calls this. An error?
pass
from a import Test, main
import a
print Test # prints 'abc'
print a.Test # prints 'abc'
main()
print Test # prints 'abc'
print a.Test # prints 'klm'
Within the python interpretor, I can produce the following:
>>> import b
abc
abc
abc
klm
In your code, you create a new variable called Test with the command from a import Test that points to the original string object. You actually want to access the Test variable owned by the module a.
In a.py you run main in the if statement:
if __name__ == "__main__":
main()
Only executes main() if that is the main script. When you import the module all the code in the if block is not run because it is not the main script. To have the main method be called remove the if statement or just call main in b.py.

Categories

Resources