Daniel and I have just discovered what is possibly the biggest, nastiest wart in Python today.
Here’s a sample snippet. Consider a module “a.py”
import b
__all__ =['Foo', ]
class Foo(object):
def __init__(self):
print "foo instantiated"
if __name__ == '__main__':
Foo()
and another module “b.py”
import a
__all__ =['Bar', ]
class Bar(object): pass
Now try to run “a.py”. You should have no problems.
Now try this “a.py”
from b import Bar
__all__ =['Foo', ]
class Foo(object):
def __init__(self):
print "foo instantiated"
if __name__ == '__main__':
Foo()
and this “b.py”
from a import Foo
__all__ =['Bar', ]
class Bar(object): pass
Now try to run “a.py”. You’ll get this
Traceback (most recent call last):
File "a.py", line 1, in ?
from b import Bar
File "/private/tmp/b.py", line 1, in ?
from a import Foo
File "/private/tmp/a.py", line 1, in ?
from b import Bar
ImportError: cannot import name Bar
What the hell is happening here? It’s kind of makes sense if you
really think about it, but it’s not obvious at all.
Python doesn’t make a distinction between declaring something and
executing something. There basically isn’t any declaration going on
in Python. Here what I think is happening - the only resource I can
find with reference to this problem is the effbot’s Import
Confusion page.
The “from foo import baz” style of import is actually checking the
modules in memory to see if a particular symbol exists. If it doesn’t
then Python tries to import the module.
When you do a circular reference - according to that page I listed -
you get an empty entry in the sys.modules. So the second time
around when you get to “from b import Bar” - there is already a module
‘b’ in sys.modules and Bar was never defined - so the interpreter
blows up.
What is more frustrating is that you can avert the whole problem by
just avoiding “from foo import baz” altogether. If you have a deep
package/module structure - you can use :
import mypackage.mymodule as mymod
and I’m quite sure you’ll never get this problem.
Now I’ve got to go change literally hundreds of files to solve this
circular import problem.
Moral of the story:
Never use “from foo import baz”. Always always always use the “import mypackage” form of import.
The obvious downside of this is that the beginning of your Python scripts will be quite noisy. But heck - what are you going to do about that?