How to avoid circular imports in Python? [duplicate]

☆樱花仙子☆ 提交于 2019-11-26 11:34:12

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

Consider a.py:

import b

class A:
    def bar(self):
        return b.B()

and b.py:

import a

class B:
    def bar(self):
        return a.A()

This works perfectly fine.

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

/package
    __init__.py
    a.py
    b.py

There are several ways to import a module in python

import package.a           # Absolute import
import package.a as a_mod  # Absolute import bound to different name
from package import a      # Alternate absolute import
import a                   # Implicit relative import (deprecated, py2 only)
from . import a            # 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. However, you still have several options when dealing with circular dependencies.

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.

Use Absolute Imports

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 a
from . import b

In a.py

import package

def func():
    package.b.some_object()

In b.py

import package

def func():
    package.a.some_object()

So those are your options (and they all kinda suck IMO). Frankly, this seems to be a glaring bug in the python import machinery, but thats just my opinion.

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.b
b_mod = lambda: main.sub.b

class A():
    def __init__(self):
        print('in class "A":', b_mod().B.__name__)

main/sub/b.py

import main.sub.a
a_mod = lambda: main.sub.a

class B():
    def __init__(self):
        print('in class "B":', a_mod().A.__name__)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!