Experiment trying to avoid Python circular dependencies -
i have testing environment try understand how python circular dependencies can avoided importing modules import x
statement, instead of using from x import y
:
test/ __init__.py testing.py a/ __init__.py m_a.py b/ __init__.py m_b.py
the files have following content:
testing.py:
from a.m_a import
m_a.py:
import b.m_b print b.m_b class a: pass
m_b.py:
import a.m_a print a.m_a class b: pass
there situation can't understand:
if remove print statements modules m_a.py
, m_b.py
or m_b.py
works ok, if print present @ m_b.py
, following error thrown:
file "testing.py", line 1, in <module> a.m_a import file "/home/enric/test/a/m_a.py", line 1, in <module> import b.m_b file "/home/enric/test/b/m_b.py", line 3, in <module> print a.m_a attributeerror: 'module' object has no attribute 'm_a'
do have ideas?
it "works" print statements removed because you're not doing depends on imports. it's still broken circular import.
either run in debugger, or add print
statement after each line, , you'll see happens:
- testing.py:
from a.m_a import a
- a.m_a:
import b.m_b
- b.m_b:
import a.m_a
- b.m_b:
print a.m_a
it's trying access a.m_a
before module finished importing. (in fact, can see rest of a.m_a
on stack in backtrace.)
if dump out sys.modules
@ point, you'll find 2 partial modules named a
, a.m_a
, if dir(a)
, there's no m_a
there yet.
as far can tell, fact m_a
doesn't added a
until m_a.py
finishes evaluating not documented anywhere in python 2.7 documentation. (3.x has more complete specification of import process—but it's different import process.) so, can't rely on either failing or succeeding; either 1 legal implementation. (but happens fail in @ least cpython , pypy…)
more generally, using import foo
instead of from foo import bar
doesn't magically solve circular-import problems. solves 1 particular class of circular-import problems (or, rather, makes class moot). (i realize there misleading text in the faq this.)
there various tricks work around circular imports while still letting have circular top-level dependencies. really, it's simpler rid of circular top-level dependencies.
in toy case, there's no reason a.m_a
depend on b.m_b
@ all. if need prints out a.m_a
, there better ways independent package!
in real-life code, there stuff in m_a
m_b
needs , vice-versa. usually, can separate out 2 levels: stuff in m_a
needs m_b
, , stuff in m_a
that's needed m_b
. so, split 2 modules. it's same thing common fix bunch of modules try reach , import main
: split utils
off main
.
what if there m_b
needs m_a
, needs m_b
? well, in case, may have insert level of indirection. example, maybe can pass thing-from-m_b
function/constructor/whatever m_a
, can access local parameter value instead of global. (it's hard more specific without more specific problem.)
if worst comes worst, , can't remove import via indirection, have move import out of way. may again mean doing import inside function call, etc. (as explained in faq after paragraph set off), or moving code above import, or kinds of other possibilities. consider these last-ditch solutions can't designed cleanly, not roadmap follow designs.
Comments
Post a Comment