FAQ 1.7.3 : How can I have modules that mutually import each other

M

MackS

Hi

I'm new to Python, I've read the FAQ but still can't get the following
simple example working:

# file main_mod.py:

global_string = 'abc'

def main():

import auxiliary_mod
instance = auxiliary_mod.ClassA()
instance.fun()
return

main()

# file auxiliary_mod.py:

class ClassA:

def fun(self):

import main_mod

print 'this is ClassA.fun() and global_string is ' +
main_mod.global_string

return

In words, the problem is: I've a main module which defines a global
variable and instantiates a class defined in a second module, and a
method of that class needs to access the global variable defined in the
main module.

When I run main_mod.py the method is executed twice:

this is ClassA.fun() and global_string is abc
this is ClassA.fun() and global_string is abc

How can I avoid this problem even in this simple example? If I move the
import main_mod statement to the outside of the definion of ClassA I
get an exception:

Traceback (most recent call last):
File "./main_mod.py", line 4, in ?
import auxiliary_mod
File "/manel/ewt/test/auxiliary_mod.py", line 4, in ?
import main_mod
File "/manel/ewt/test/main_mod.py", line 13, in ?
main()
File "/manel/ewt/test/main_mod.py", line 9, in main
instance = auxiliary_mod.ClassA()
AttributeError: 'module' object has no attribute 'ClassA'


As far as I can tell I'm following the technique suggested by Guido
himself to handle mutual imports:

"Guido van Rossum recommends avoiding all uses of from <module> import
...., and placing all code inside functions. Initializations of global
variables and class variables should use constants or built-in
functions only. This means everything from an imported module is
referenced as <module>.<name>."

[http://www.python.org/doc/faq/programming.html]

How can I get this simple example to work?

Thank you for any help in advance,

Mack
 
R

Reinhold Birkenfeld

MackS said:
Hi

I'm new to Python, I've read the FAQ but still can't get the following
simple example working:

# file main_mod.py:

global_string = 'abc'

def main():

import auxiliary_mod
instance = auxiliary_mod.ClassA()
instance.fun()
return

main()

# file auxiliary_mod.py:

class ClassA:

def fun(self):

import main_mod

print 'this is ClassA.fun() and global_string is ' +
main_mod.global_string

return

In words, the problem is: I've a main module which defines a global
variable and instantiates a class defined in a second module, and a
method of that class needs to access the global variable defined in the
main module.
[...]
How can I get this simple example to work?

You suffer from a circular dependency issue. The general solution in
this case is to factor out code into a third module. In your case,
consider putting global_string or main() in a third module that you can
import from auxiliary_mod.

Reinhold
 
J

John Machin

Hi

I'm new to Python, I've read the FAQ but still can't get the following
simple example working:

# file main_mod.py:
global_string = 'abc'
def main():
import auxiliary_mod
instance = auxiliary_mod.ClassA()
instance.fun()
main()

# file auxiliary_mod.py:
class ClassA:
def fun(self):
import main_mod
print 'this is ClassA.fun() and global_string is ' +
main_mod.global_string

In words, the problem is: I've a main module which defines a global
variable and instantiates a class defined in a second module, and a
method of that class needs to access the global variable defined in the
main module.

Needs??? Sorry to be blunt, but this is an intrinsically ludicrous
concept, in *any* language. The whole idea of modules is (wait for it)
"modularity". "A imports B which imports A" is an utter nonsense.
When I run main_mod.py the method is executed twice:

Care needs to be taken in general with a module that is to be RUN as a
script as well as imported; for further details, and an explanation of
why your method is executed twice, see sample code at the end of my
post. Please note: I'm showing you this because it's something you
need to know about Python quite *independently* of the fubaristic
circular import thing.
How can I avoid this problem even in this simple example?

Don't have circular imports.
How can I get this simple example to work?

You shouldn't try. Refactor. NOTE: "executed once only" doesn't mean
that it is "working".

=== mainmod.py ===
global_string = 'abc'

def main():
import auxmod
instance = auxmod.ClassA()
instance.fun()
# don't need this: return

print "Statements are also EXECUTED when the module is imported."
print "Best to avoid statements with side-effects."
main()
print "Got the picture now?"

if __name__ == "__main__":
print "Being run as a script i.e. not imported."
main()
print "Done ..."

=== auxmod.py ===
trip_count = 0
class ClassA:
def fun(self):
global trip_count
trip_count += 1
saved_tc = trip_count
print 'ClassA.fun -- before importing mainmod; tc =',
trip_count
import mainmod
print 'ClassA.fun -- after importing mainmod; tc =',
trip_count, 'saved_tc =', saved_tc
print 'global_string is', mainmod.global_string
 
J

John Roth

Circular import dependencies don't work well; depending
on the exact conditions they can leave you pulling your hair
out for hours. In your example, just pull the global variable
out into a third module and have both of your major
modules import and reference it from there.

In general, you should never have circular imports at
load time. There are three general ways of handling
the issue:

1. Redesign to eliminate the circular dependencies.
2. Put the classes with the circular dependencies in
the same module.
3. Load the backlinks at run time.

I may be old fashioned, but I put all of the import
statements at the top of the module. Putting them
into the middle of functions obscures the structure
of the code, at least to my eyes. I'd rather have them
all in one place.

I've got a Java package (Fit Library) that I'm porting
to Python that has a number of very nasty circular
import dependencies that I can't structure out easily:
they're fundamental to the functionality. I'm taking
the "load at run time" option, which works well since
the basic FIT package has a very competent dynamic
loader.

However, all of this is kind of advanced functionality.
Most applications don't require that kind of mess.

John Roth








MackS said:
Hi

I'm new to Python, I've read the FAQ but still can't get the following
simple example working:

# file main_mod.py:

global_string = 'abc'

def main():

import auxiliary_mod
instance = auxiliary_mod.ClassA()
instance.fun()
return

main()

# file auxiliary_mod.py:

class ClassA:

def fun(self):

import main_mod

print 'this is ClassA.fun() and global_string is ' +
main_mod.global_string

return

In words, the problem is: I've a main module which defines a global
variable and instantiates a class defined in a second module, and a
method of that class needs to access the global variable defined in the
main module.

When I run main_mod.py the method is executed twice:

this is ClassA.fun() and global_string is abc
this is ClassA.fun() and global_string is abc

How can I avoid this problem even in this simple example? If I move the
import main_mod statement to the outside of the definion of ClassA I
get an exception:

Traceback (most recent call last):
File "./main_mod.py", line 4, in ?
import auxiliary_mod
File "/manel/ewt/test/auxiliary_mod.py", line 4, in ?
import main_mod
File "/manel/ewt/test/main_mod.py", line 13, in ?
main()
File "/manel/ewt/test/main_mod.py", line 9, in main
instance = auxiliary_mod.ClassA()
AttributeError: 'module' object has no attribute 'ClassA'


As far as I can tell I'm following the technique suggested by Guido
himself to handle mutual imports:

"Guido van Rossum recommends avoiding all uses of from <module> import
..., and placing all code inside functions. Initializations of global
variables and class variables should use constants or built-in
functions only. This means everything from an imported module is
referenced as <module>.<name>."

[http://www.python.org/doc/faq/programming.html]

How can I get this simple example to work?

Thank you for any help in advance,

Mack
 
D

david.tolpin

Needs??? Sorry to be blunt, but this is an intrinsically ludicrous
concept, in *any* language. The whole idea of modules is (wait for it)
"modularity". "A imports B which imports A" is an utter nonsense.

There is such a thing called 'recursion'. Self-recursion is when
function x calls itself to execute the same action on a partial result
of its previous invocation. This technique is a natural expression of
what such ugly constructs as for and while are for in some programming
languages. Indirect recusion is when function x calls y, and function y
calls x. It is often happens in state automata.

A static equivalent of indirect recursion is dependencies between
declarations, when declaration x depends on y, and declaration y
depends on x. Imagine a design
of a literate programming environment, where both documentation and
text are structured, and either can contain the other. A natural design
for that is to have two modules, one implementing the parser for
documentation, the other implementing the parser for code, each
referencing the other so that both parsers can call each other when
they need to switch context.

In a well-designed programming language, circular intermodule
dependencies are normal, because this is a natural high-level
abstraction to express such relations. Because of primitive
implementation of modular system in Python, circular dependencies are
not possible, and things which are normally done automatically in a
higher-level language, need to be coded manually in Python.

In particular, to bypass this deficiency of Python for a problem
similar to one described above, one has to define base classes for each
of the two parsers, and make the implementations depend on the other
implementation's base class, not on itself, thus splitting the
interface and the implementation where it is not only unnecessary but
outright wrong if clean design is taken into consideration.

I'd wish a cleaner and more consistent implementation of packages
existed in Python; but unfortunately too many things in current Python
are far from being on the level a modern programming language (such as
Common Lisp, for example) demands.

David Tolpin
http://davidashen.net/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top