structables

Python
uv: Unified Python packaging
guillaume-chevalier/How-to-Grow-Neat-Software-Architecture-out-of-Jupyter-Notebooks: Growing the code out of your notebooks - the right way.
Notebook on nbviewer
Notebook on nbviewer
Notebook on nbviewer
Notebook on nbviewer
PEP 8 – Style Guide for Python Code | peps.python.org
Erlkönig: Commandname Extensions Considered Harmful
How to avoid circular imports in Python?
389
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 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).
1. Errors importing modules with circular imports
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 imported module.
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 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 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()
2. Errors using imported objects with circular dependencies
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:
package/a.py
import package.b
def func_a():
return "a"
package/b.py
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
package/a.py
import package.b
class A(object):
pass
package/b.py
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.
Share
Improve this answer
Follow
edited Oct 14, 2019 at 18:18
answered May 9, 2016 at 23:05
Brendan Abel
36.9k15
15 gold badges
94
94 silver badges
124
124 bronze badges
20
Brendan, this is an amazingly through answer! I have read a hundred answers about solving circular imports, finally I get it. thx! By the way, you might want to add one more solution to your list: set global for each package to None at top of file, then inject module into global at runtime. This has advantage that all module names are at top of file. –
Dan Oblinger
Commented
May 28, 2016 at 15:51
1
You can somewhat combine the first and second approaches by using absolute imports (so you can see all the dependencies at the top) and naming the components you want at point-of-use. Something like import package;def func(): a = package.a in lieu of from package import a –
Alex H.
Commented
Jul 28, 2016 at 18:34
3
Would deferring import until inside a function also be slower, in case the function is repeatedly called? O
🎥 Stream your favorite movie from the terminal! | PythonRepo
#164: Use type hints to build your next CLI app
The Best way to build a Python command line tool - Python Typer Tutorial
Building Beautiful Command Line Interfaces with Python
FastAPI
Python Web Scraping Tutorial - GeeksforGeeks
AIOHTTP
Qt Designer Download for Windows and Mac
pyqt/examples: Learn to create a desktop app with Python and Qt
Git Cola: The highly caffeinated Git GUI
git-cola/git-cola: git-cola: The highly caffeinated Git GUI
mherrmann/fbs-tutorial: Tutorial for creating Python/Qt GUIs with fbs
Manual | fman build system
pyqt5 · GitHub Topics
truenathan/PythonWeb: Utilising Q6t’s Powerful web engine and the super easy, high-level programming language of Python, PythonWeb is, at the heart, a simple browser made for your Windows computer.
MechanicalSoup/MechanicalSoup: A Python library for automating interaction with websites.
weblyzard/inscriptis: A python based HTML to text conversion library, command line client and Web service.
mammothb/symspellpy: Python port of SymSpell: 1 million times faster spelling correction & fuzzy search through Symmetric Delete spelling correction algorithm
Programmatic access
Supabase Python Client - Introduction