How to avoid circular imports in Python? [duplicate] How to avoid circular imports in Python? [duplicate] python python

How to avoid circular imports in Python? [duplicate]


Consider the following example python package where a.py and b.py depend on each other:

/package    __init__.py    a.py    b.py

Types of circular import problems

Circular import dependencies typically fall into two categories dependingon what you're trying to import and where you're using it inside eachmodule. (And whether you're using python 2 or 3).

1. Errors importing modules with circular imports

In some cases, just importing a module with a circular import dependencycan result in errors even if you're not referencing anything from theimported module.

There are several standard ways to import a module in python

import package.a           # (1) Absolute importimport package.a as a_mod  # (2) Absolute import bound to different namefrom package import a      # (3) Alternate absolute importimport 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 ImportError or 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.

EDIT: The ImportError and AttributeError issues 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.

Absolute Import

Just use the first import syntax above. The downside to this method is that the import names can get super long for large packages.

In a.py

import package.b

In b.py

import package.a

Defer import until later

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.

In a.py

def func():    from package import b

In b.py

def func():    from package import a

Put all imports in a central module

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.

In __init__.py

from . import afrom . import b

In a.py

import packagedef func():    package.b.some_object()

In b.py

import packagedef func():    package.a.some_object()

2. Errors using imported objects with circular dependencies

Now, while you may be able to import a module with a circular importdependency, you won't be able to import any objects defined in the moduleor actually be able to reference that imported module anywherein 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:

package/a.py

import package.bdef func_a():    return "a"

package/b.py

import package.adef 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

package/a.py

import package.bclass A(object):    pass

package/b.py

import package.a# package.a is referenced at the top level of the moduleclass 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 possibleto refactor or reorganize the code to prevent these errors and move module references inside a code block.


Only import the module, don't import from the module:

Consider a.py:

import bclass A:    def bar(self):        return b.B()

and b.py:

import aclass B:    def bar(self):        return a.A()

This works perfectly fine.


We do a combination of absolute imports and functions for better reading and shorter access strings.

  • Advantage: Shorter access strings compared to pure absolute imports
  • Disadvantage: a bit more overhead due to extra function call

main/sub/a.py

import main.sub.bb_mod = lambda: main.sub.bclass A():    def __init__(self):        print('in class "A":', b_mod().B.__name__)

main/sub/b.py

import main.sub.aa_mod = lambda: main.sub.aclass B():    def __init__(self):        print('in class "B":', a_mod().A.__name__)