I know the issue of circular imports in python has come up many times before and I have read these discussions. The comment that is made repeatedly in these discussions is that a circular import is a sign of a bad design and the code should be reorganised to avoid the circular import.
Could someone tell me how to avoid a circular import in this situation?: I have two classes and I want each class to have a constructor (method) which takes an instance of the other class and returns an instance of the class.
More specifically, one class is mutable and one is immutable. The immutable class is needed
for hashing, comparing and so on. The mutable class is needed to do things too. This is similar to sets and frozensets or to lists and tuples.
I could put both class definitions in the same module. Are there any other suggestions?
A toy example would be class A which has an attribute which is a list and class B which has an attribute which is a tuple. Then class A has a method which takes an instance of class B and returns an instance of class A (by converting the tuple to a list) and similarly class B has a method which takes an instance of class A and returns an instance of class B (by converting the list to a tuple).
Only import the module, don’t import from the module:
import b class A: def bar(self): return b.B()
import a class B: def bar(self): return a.A()
This works perfectly fine.
Consider the following example python package where
b.py depend on each other:
/package __init__.py a.py b.py
Circular import dependencies typically fall into two categories depending
on what you’re trying to import and where you’re using it inside each
module. (And whether you’re using python 2 or 3).
In some cases, just importing a module with a circular import dependency
can result in errors even if you’re not referencing anything from the
There are several standard ways to import a module in python
import package.a # (1) Absolute import import package.a as a_mod # (2) Absolute import bound to different name from package import a # (3) Alternate absolute import import a # (4) Implicit relative import (deprecated, python 2 only) from . import a # (5) Explicit relative import
Unfortunately, only the 1st and 4th options actually work when you
have circular dependencies (the rest all raise
AttributeError). In general, you shouldn’t be using the
4th syntax, since it only works in python2 and runs the risk of
clashing with other 3rd party modules. So really, only the first
syntax is guaranteed to work.
AttributeErrorissues only occur in
python 2. In python 3 the import machinery has been rewritten and all
of these import statements (with the exception of 4) will work, even with
circular dependencies. While the solutions in this section may help refactoring python 3 code, they are mainly intended
for people using python 2.
Just use the first import syntax above. The downside to this method is
that the import names can get super long for large packages.
I’ve seen this method used in lots of packages, but it still feels
hacky to me, and I dislike that I can’t look at the top of a module
and see all its dependencies, I have to go searching through all the
functions as well.
def func(): from package import b
def func(): from package import a
This also works, but has the same problem as the first method, where
all the package and submodule calls get super long. It also has two
major flaws — it forces all the submodules to be imported, even if
you’re only using one or two, and you still can’t look at any of the
submodules and quickly see their dependencies at the top, you have to
go sifting through functions.
from . import a from . import b
import package def func(): package.b.some_object()
import package def func(): package.a.some_object()
Now, while you may be able to import a module with a circular import
dependency, you won’t be able to import any objects defined in the module
or actually be able to reference that imported module anywhere
in the top level of the module where you’re importing it. You can,
however, use the imported module inside functions and code blocks that don’t
get run on import.
For example, this will work:
import package.b def func_a(): return "a"
import package.a def func_b(): # Notice how package.a is only referenced *inside* a function # and not the top level of the module. return package.a.func_a() + "b"
But this won’t work
import package.b class A(object): pass
import package.a # package.a is referenced at the top level of the module class B(package.a.A): pass
You’ll get an exception
AttributeError: module ‘package’ has no attribute ‘a’
Generally, in most valid cases of circular dependencies, it’s possible
to refactor or reorganize the code to prevent these errors and move
module references inside a code block.
We do a combination of absolute imports and functions for better reading and shorter access strings.
import main.sub.b b_mod = lambda: main.sub.b class A(): def __init__(self): print('in class "A":', b_mod().B.__name__)
import main.sub.a a_mod = lambda: main.sub.a class B(): def __init__(self): print('in class "B":', a_mod().A.__name__)