Circular package references

Thomas Wouters thomas at xs4all.net
Tue Aug 8 09:20:07 EDT 2000


On Tue, Aug 08, 2000 at 01:21:12PM +0200, Serge Beaumont wrote:

> I've looked everywhere: the docs, FAQs, dejanews and c.l.py. But I can't
> find an answer to my questions:

> - Are circular package references allowed in Python? I think not, from
> the exceptions I get... or there's something I'm not doing right.

They are allowed, but they can be tricky. Here's how it works:

Say you have two modules, 'Spam' and 'Eggs'. Module Spam looks something
like this:

import Eggs
class Spam:
	....
def doSpam():
	....
def doSpamAndEggs():
	Eggs.SpamAndEggs(...)
	....

and Eggs looks something like this:

import Spam
class Eggs:
	....

class SpamAndEggs(Spam.Spam, Eggs):
	....


Now, if you import 'Spam', the interpreter just starts running the code in
'Spam'. The first statement it sees is 'import Eggs', so it starts loading
'Eggs'. And the first statement in 'Eggs' is 'import Spam'. However, it sees
'Spam' is already in the modules list, so it doesn't have to be imported
again. That it hasn't finished importing is not important to the
interpreter.

So the execution of 'Eggs' continues, it defines the class Eggs, and then
tries to define SpamAndEggs -- and it references 'Spam.Spam'. But
'Spam.Spam', the 'Spam' class in the 'Spam' module, hasn't been defined
yet, because execution of the module 'Spam' hasn't reached that code yet. So
you get an attribute error on 'Spam.Spam'.

The solution is usually to do the importing at the last possible moment, in
the face of circular imports. In the above example, moving 'import Eggs'
into the 'doSpamAndEggs()' function would solve the problem: the import is
done when the function executes, not when it's being created.

(This hardly matters for performance, because the 'import' of an already
imported module is a simple name-binding routine. The already-instantiated
'Eggs' module is just used again.)

Another solution is to move the 'import' to the bottom of the file, rather
than the top. This doesn't matter if you only use the module inside
functions, like 'Spam' does, but it wouldn't help for the 'Eggs' module,
because it needs the 'Spam' module in the definition of a class.

> - When two classes reference each other, MUST they be in the same
> package?

No, but their names must be 'qualified' so that the interpreter can find
them, and you need to resolve circular references as above.

> - I don't understand the global keyword either. Could this be a help
> here?

'global' has not much bearing on the above discussion. 'global' is used to
tell non-global scopes (functions and class definitions) that a variable is
a global variable, *even though* it's being assigned to. Normally, a
variable is local if it's assigned to in the code block, or global
otherwise. If you want to assign to a global inside a function or class,
like so:

X = 0

def setX(x):
	X = x

it won't work, because the 'X' you assign to in 'setX' is the *local* X, not
the global one. The problem is more obvious if you try to read the global X
before you write to the local one, like this:

def addX(x):
	X = X + x

You'll get an 'UnboundLocalError', because you use a local variable before
you write a value to it. If you use the 'global' keyword to 'define' X, both
work as expected, because the interpreter knows it should operate on the
global 'X', not the local one:

def setX(x):
	global X
	X = x

def addX(x):
	global X
	X = X + x

> My situation:
> thing.py -> class Thing -> imports allThings
> thinglist.py -> class ThingList -> imports Thing
> globals.py -> allThings (variable) -> imports ThingList

Can't say much 'bout that without seeing where you do the import, and why,
but try to move the imports about and it might just work ! :)

-- 
Thomas Wouters <thomas at xs4all.net>

Hi! I'm a .signature virus! copy me into your .signature file to help me spread!




More information about the Python-list mailing list