Aditya Sriram
Aditya Sriram

Reputation: 453

Access functions of a module from another module which itself imports the first module

Sorry for the confusing title, I tried to fit as much of the problem in the title.


The scenario:

I have a module(say ModuleA) which contains class definitions of some Tkinter GUI elements. These elements also have certain events/functions bound/binded with them(such as '<Button-1>','<Button-2>'....

Now there's another module(say ModuleB). This is the main(core of the) program. In this module I import ModuleA to use its objects. The objects of ModuleA have a place in an array(say Array1); and also there is another array(say Array2) which stores the value of just one of the data-member of each of the objects of Array1, and these are the data-members which are manipulated by the Event Bindings.

So the problem is that when an Event occurs, the objects(stored in Array1) of ModuleA respond visually as needed, but in the backend their corresponding data-member values must also be updated in Array2 accordingly.


In short:

#ModuleA.py

from ModuleB import foo

class bar
    data = 1
    # some tkinter code 
    # bind mouse click to function foo of ModuleB
    
-------------------------------------------------------

#ModuleB.py

from ModuleA import bar

Array1 = [objects of class bar]
Array2 = [value of data of objects in Array1]

def foo(#obj of class bar)
    # find index of bar object which called this function in Array1
    # accordingly change Array2

What I tried:

In the Event Bindings of the objects of ModuleA I added the required function(say foo) which will handle the required array manipulations and is defined in ModuleB as it has to handle the Array2 of ModuleB. But this gave me an error global name 'foo' is not defined

So in the class-definition of ModuleA I added global foo, which too didn't solve it.

Finally I tried inserting in ModuleA

from ModuleB import foo

Which raised an ImportError saying it cannot import foo(which I guess is because ModuleB itself is importing ModuleA hence circular reference)


A solution

One solution clearly visible is to copy entire ModuleA (containing class definitions) to ModuleB. But this is not always practical and not too pythonic either.

Please help.

Upvotes: 0

Views: 180

Answers (3)

Ketouem
Ketouem

Reputation: 3857

You should check this link out : http://effbot.org/zone/import-confusion.htm There is an interesting paragraph about how to handle circular reference by moving the import statement at the end of the module. But IMHO don't try to handle that thing, just refactor your code ;)

Upvotes: 0

Blckknght
Blckknght

Reputation: 104712

Circular imports in Python can be confusing. It's easy to get it wrong, and so it's usually best to avoid them, if possible. However, they are not illegal, and if you're careful they can work just fine.

The import process works like this, when you type import foo:

  1. First, Python checks in the sys.modules dictionary to see if the module foo has been loaded already. If so, the pre-existing module is simply placed into your current module's namespace (and the process stops there).
  2. If not, it checks if foo.py file can be found to load. If none exists, an ImportError is raised and the process stops.
  3. If a file was found, a new, empty module object is created and put into sys.modules under the name foo.
  4. Then, Python opens the file and begins executing it, using the module object created in step 3 as the local namespace.

Now, if the foo module described above has it's own import statements, the import process can recurse, loading up the other module before continuing on with the execution of the foo module's code. But, since the empty module object get added to the sys.modules dictionary before any more imports are processed, it will never loop all the way around and try to load the same file more than once (in most cases).

There are a few things that can go wrong.

If foo is being executed as a script (rather than being imported by some other module), it's initial entry in sys.modules will be under the name __main__ rather than foo. This means that if foo gets imported from somewhere else, you'll end up with two copies of the same code. This is an exception to the general rule that a module doesn't get loaded twice, and it can catch you off guard in some cases.

Also, if your circularly imported modules are making top-level accesses to each other, you'll run into trouble if they're imported in the wrong order (or any order, if they both reference each others contents in the wrong way from the top level). You can avoid most problems by ensuring that as little code runs at module load time as possible (put it into functions instead!). Then call the functions from a well defined entry point, after everything has been loaded.

So, in your situation, here are things I'd examine:

  1. Do you need to be doing from moduleB import foo? Can you instead doimport moduleB and access moduleB.foo later on? This would be the easiest fix that could work (though it will not fix all possible problems).
  2. Do you need to be doing work at the top level of your modules? Often you can put object creation into a factory function, rather than at top level (which makes solution #1 work more often). This can be a good design idea anyway, since it is easier to extend later if you want to be able to alter (or customize) the way they objects are created at run time (you just write a second function that works differently, or add arguments to the factory).
  3. Can you move a part of the code from ModuleA or ModuleB into a third module, which doesn't need to import either of them, and so eliminate the need for the circular import? This is often a good idea, as it lets you sidestep the whole issue of what gets loaded when.

Edit: here's what a fixed version of your modules might look like:

ModuleA.py:

import ModuleB

class bar():
    def __init__(self, data):
        self.data = data
        # do something here that accesses ModuleB.foo

ModuleB.py:

import ModuleA

def foo():
    # do whatever

Array1 = [ModuleA.bar(i) for i in range(10)]
Array2 = [whatever]

main.py:

import ModuleB # import order is important here!
import ModuleA

if __name__ == "__main__":
    # do stuff

This should be the minimal fix. ModuleA only accesses ModuleB.foo from within the bar class's __init__ method, so the circular imports will work OK as long as ModuleB.foo is defined before any bar instances are created.

If you want the imports to work in any order, it's a bit trickier. You don't want any work being done at the top-level of the modules:

ModuleA.py is as above.

ModuleB.py:

import ModuleA

def foo():
    #do stuff

def setupArrays():
    global Array1, Array2 # lets us create these global variables

    Array1 = [ModuleA.bar(i) for i in range(10)]
    Array2 = [whatever]

main.py:

import ModuleA, ModuleB  # import order doesn't matter

if __name__ == "__main__":
    ModuleB.setupArrays()
    # do stuff

This is still a bit of a kludge. If you can break the circular import completely, perhaps the code can be simplified in a more obvious way. For instance, we could pass the callback function as a parameter to the bar class's constructor:

ModuleA.py:

# No import statement here! Circular imports avoided!

class bar():
    def __init__(self, data, callback):
        self.data = data
        # bind stuff to call the callback function provided

ModuleB:

import ModuleA

def foo():
    # do whatever

Array1 = [ModuleA.bar(i, foo) for i in range(10)]
Array2 = [whatever]

Upvotes: 1

neotam
neotam

Reputation: 2731

Actually you are doing wrong , for suppose take two modules moduleA and moduleB. If you import moduleA into the moduleB then you can't import the moduleB into moduleA if you are trying to do that thing you should get Import error.

let see The wrong way that you are doing

The below one is moduleA.py

import moduleB
print " I am moduleA"
class foo():
     pass

here is the moduleB.py

from  moduleA import foo
print "I am moduleB"

now execute moduleB.py

python moduleB.py

you will get the ImportError , but it would not happened if you do

python moduleA.py 

The above process is quit wrong, python will not allow this type of importing simply here we are creating loop of imports because on each first import the top level code of whole module will be executed of course python will not allow this .

To solve your problem just change the module hierarchy .

Here is the simple solution : If you want the content of both modules (moduleA and moduleB) just create one more module moduleC and import both modules(moduleA and moduleB) there in moduleC and do your necessary stuff.

Please refer the edited stuff , you are doing same thing

Upvotes: 1

Related Questions