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
andAttributeError
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.
In this article we will discuss the Circular Import Error that can occur when importing modules in Python, and how to solve it.
What is a Circular Import?
Circular imports in Python can be a tricky and confusing issue to deal with. A circular import occurs when two or more modules import each other, creating a looping cycle of imports that can lead to import errors. The below diagram illustrates what this looks like.
In this scenario, the Python interpreter will first encounter module A, which imports module B. However, since module B also imports module A, the interpreter will then try to import module A again. This creates a recursive loop where the interpreter keeps importing module A and module B indefinitely, without being able to complete the import process.
In this article, we will explore the various causes of circular imports and how to resolve them.
There are three major reasons for why this error can occur. The root cause for all these are the same, but the way in which they occur can vary a little. We will teach you how to identify these patterns, so that you can quickly resolve any circular import errors in your code.
Problem #1
We will start with the easiest problem to identify and solve. If you have given your Python file, the exact same name as the library you are importing, the Python interpreter will end up getting confused between the library which you actually mean to import, and your Python file (since they both have the sane name).
To fix this, all you need to do is change the name of your Python file to something else.
Here is an example you try out with the random library, where we also named our Python file “random.py”.
import random print(random.randint(0, 10))
This code will throw the following error.
Traceback (most recent call last):
File "c:UsersCodersLegacyrandom.py", line 1, in <module>
import random
File "c:UsersCodersLegacyrandom.py", line 3, in <module>
print(random.randint(0, 10))
AttributeError: partially initialized module 'random' has no attribute 'randint' (most likely due to a circular import)
Changing the file name to something like random_code.py
will make the code work.
Direct Imports
Direct imports is the problem that we described right in the start. Let’s take a look at some actual code to better understand how this problem can occur.
Here we have code snippets, one for each Python file we have created. The first one (called moduleA.py) defines a function called funcA
. The second one (called moduleB.py) defines a function called funcB
.
from moduleB import funcB def funcA(): print("I am module A") funcB()
from moduleA import funcA def funcB(): print("I am module B") funcA()
Running either of the above Python files will give us the error we got in the previous section.
The solution would be to put both functions in the same file, because their definitions depend on each other.
So something like this.
def funcB(): print("I am module B") funcA() def funcA(): print("I am module A") funcB()
No need for imports here.
It may sound a bit odd, but there really is no other way. You need to be smart about how you write your code. Pre-planning and drawing of an import graph (like the one in the start of this article) can help you identify whether you have any “cycles”.
Indirect Imports
Indirect imports are actually the same thing as direct imports. The only difference is that the chain of imports is longer. Instead of just two modules importing each other “directly” we have a chain of modules where one imports another, which imports another, and so on, until it eventually leads back to the first module. In other words, more than two modules are involved.
This can make it difficult to identify the source of the circular import, as it is not immediately obvious which modules are involved in the cycle.
Regardless, the solution here is the as the one we discussed for direct import, which would be to refactor your code.
This marks the end of the tutorial on “most likely due to a circular import” error and how to solve it. Any suggestions or contributions for CodersLegacy are more than welcome. Questions regarding the tutorial content can be asked in the comments section below.
We love to use modules in Python and why not, they provide additional functionalities, better coding practice, and always create a proper structure while using. But many times unknowingly, we can run into python circular import problems if we accidentally have another file named as module’s name. As python prefers importing from the local current directory first and then from site-packages, it will create a circular import problem.
Generally, the Python Circular Import problem occurs when you accidentally name your working file the same as the module name and those modules depend on each other. This way the python opens the same file which causes a circular loop and eventually throws an error.
For example, when you name your file as random.py and try to import “from random import randint”, it’ll throw a circular import error (also termed as from partially initialized module).
In this post, we’ll have a look at all the causes and their solutions for circular import.
How Circular Import Is Identified?
Then a module calls an object within itself, circular import error is raised. Let’s consider an example where you are working on a ‘rea.py’ python file and you write a code as ‘from rea import x’. This will cause python to import the object x from the rea module.
This will cause a circular call within the code and it’ll throw an exception with an error mentioned as “ImportError: cannot import name ‘x’ from partially initialized module ‘rea’ (most likely due to a circular import) (/home/pythonpool/rea.py)”.
This exception is raised when you try to import any type of object. There are no exceptions.
Tip: Circular import is only raised when you import object from module. It is not raised when you try to import module itself. So, in above example, “import rea” will work just fine.
How to fix Python Circular Import?
There are several workarounds to solve the circular import problem. Each of these workarounds has its different use cases. Make sure you use the solution that suits best for your problem.
Conditional Import is also one of the ways by which you can handle such cases but does not try to use try-except blocks to fix circular imports as the core problem of importing variables still remain even if we ignore it.
Importing The Module Itself
There is a good workaround Python circular import error that you can import the module itself rather than importing object/function from the module. This way you can import and use all the objects and variables from the module.
Suppose, a module named module_name has function func_name, and you want to import it but it throws a circular error.
The easiest way to make this work is by importing the module_name itself. The following example will help you to understand it –
rea.py –
import rea x=1 if __name__ == '__main__': print(rea.x)
Even if you are calling the same module, it’ll work. Use these cases to fix the issues in flask and Django where the filenames can match the pre-defined module names.
Rename Your Working file
Sometimes, we can name our working file to module name without knowing its consequences (Even I did it many times :P). Renaming the file will work perfectly in such cases. For example, if you want to use the numpy module, you should avoid your working file naming numpy.py.
Here’s an example –
– numpy.py –
from numpy import array x = array([1, 2, 3])
ImportError: cannot import name 'array' from partially initialized module 'numpy' (most likely due to a circular import) (/home/pythonpool/numpy.py)
Now, rename our working file to a different name –
– pool_numpy.py –
from numpy import array x = array([1, 2, 3]) print(x)
[1 2 3]
Just as we avoid naming variables to built-in variables to avoid issues, you need to avoid naming your file to module name to avoid conflicts.
Avoid Circular Import Calls
Consider the following example –
– module1.py –
from module2 import func2 def func1(): func2()
– module2.py –
from module1 import func1 def func2(): func1()
Command to run –
python .module1.py
Traceback (most recent call last):
File "/home/pythonpool/module1.py", line 1, in <module>
from module2 import func2
File "/home/pythonpool/module2.py", line 1, in <module>
from module1 import func1
File "/home/pythonpool/module1.py", line 1, in <module>
from module2 import func2
ImportError: cannot import name 'func2' from partially initialized module 'module2' (most likely due to a circular import) (/home/pythonpool/module2.py)
The above example demonstrates a situation where you try to import a variable from module2 which is partially initialized (python doesn’t know that func2 even exists or not).
In such cases try to copy the required function/object to your working file.
In the above example, if you declare func2 itself in module1, it’ll not be of any problem.
Solve Circular Import Error In Django
Ever tried importing Django modules/classes in your Django project? You’ll definitely encounter a python circular import error once in such scenarios. If you try to implement the Django model manually by directly importing it, it’ll throw an error.
For example, you have your Django installed apps as follows –
INSTALLED_APPS = (
'app1',
)
And you want to use the structure of app1, you might import it directly in your python file considering its installed app. Something like this –
from app1.models import App1
Then most likely, you’ll encounter the python circular import error in your code. This is the incorrect way of importing a Model in your Django Application. Following is the correct way to do it –
For Django <=1.7:
from django.db.models import get_model MyModel = get_model('app1', 'App1')
For Django > 1.7:
from django.apps import apps apps.get_model('app1.App1')
This way the model ‘App1’ from app ‘app1’ will be imported into your Django application directly.
Solution for Python Circular Import Error In Flask
Similar to Django, flask also has circular import issues especially when you’re dealing with SQLAlchemy. If you try to use the database model of SQLAlchemy (declared in the main app file) in a different file, it’ll throw a circular import error.
Consider the following examples –
– app.py –
from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__ name __) db = SQLAlchemy(app) from models import routes
– routes.py –
This is indeed a circular import as app.py called routes and routes call DB from app.py. To fix such issues we create a separate extension for a database where we initialize the SQLAlchemy database.
from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy()
This can be imported in app.py and models.py and used accordingly.
FAQs
Is there any python circular import detector?
Pycycle is a Github project which aims to detect circular import problems in your code. This tool has good command-line usage with multiple arguments usage.
Importing Static Variable at class level cause circular import?
Yes, if there is another import statement calling working module again, then it may cause circular import.
What causes circular import problems in __init__.py file?
__init__ file is responsible for importing and initializing packages. During this process, we always tend to import other modules from the package. So, if your other module calls back to another module that is yet to initialize in __init__, it’ll throw a circular import.
References
Python Import System: How searching of Modules works in Python.
Trending Python Articles
-
Efficiently Organize Your Data with Python Trie
●May 2, 2023
-
[Fixed] modulenotfounderror: no module named ‘_bz2
by Namrata Gulati●May 2, 2023
-
[Fixed] Cannot Set verify_mode to cert_none When check_hostname is Enabled
by Namrata Gulati●May 2, 2023
-
Prevent Errors with Python deque Empty Handling
by Namrata Gulati●May 2, 2023
Новичок в python. Пытался разобраться в colorama и написал такой код:
from colorama import *
init()
print(Style.DIM)
print("Это рыбный текст в к0тором я поместил все буквы алфавита. Читать его не имеет смысла так, как мне лень дальше писать")
print(Style.RESET_ALL)
print(Fore.GREEN)
print("Это рыбный текст в к0тором я поместил все буквы алфавита. Читать его не имеет смысла так, как мне лень дальше писать")
print(Style.RESET_ALL)
print(Back.YELLOW)
print("Это рыбный текст в к0тором я поместил все буквы алфавита. Читать его не имеет смысла так, как мне лень дальше писать")
print(Style.RESET_ALL)
"""
Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET.
Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET.
Style: DIM, NORMAL, BRIGHT, RESET_ALL
"""
Запускаю… Ошибка
NameError: name ‘init’ is not defined
Убираю init()
Та же ошибка,
Заменяю код на
from colorama import init
init()
print(Style.DIM)
print("Это рыбный текст в к0тором я поместил все буквы алфавита. Читать его не имеет смысла так, как мне лень дальше писать")
print(Style.RESET_ALL)
print(Fore.GREEN)
print("Это рыбный текст в к0тором я поместил все буквы алфавита. Читать его не имеет смысла так, как мне лень дальше писать")
print(Style.RESET_ALL)
print(Back.YELLOW)
print("Это рыбный текст в к0тором я поместил все буквы алфавита. Читать его не имеет смысла так, как мне лень дальше писать")
print(Style.RESET_ALL)
Ошибка
ImportError: cannot import name ‘init’ from partially initialized module ‘colorama’ (most likely due
to a circular import)
Циклическая зависимость в Python возникает, когда два или более модуля зависят друг от друга. Это связано с тем, что каждый модуль определяется в терминах другого.
Например:
functionA(): functionB()
functionB(): functionA()
Приведенный выше код демонстрирует довольно очевидную циклическую зависимость. functionA() вызывает functionB(), следовательно, в зависимости от него, а functionB() вызывает functionA(). У этого типа циклической зависимости есть некоторые очевидные проблемы, которые мы опишем немного дальше в следующем разделе.
Проблемы с круговыми зависимостями
Циклические зависимости могут вызвать множество проблем в вашем коде. Например, это может привести к тесной связи между модулями и, как следствие, к снижению возможности повторного использования кода. Этот факт также усложняет сопровождение кода в долгосрочной перспективе.
Кроме того, циклические зависимости могут быть источником потенциальных сбоев, таких как бесконечные рекурсии, утечки памяти и каскадные эффекты. Если вы не будете осторожны и у вас есть циклическая зависимость в вашем коде, может быть очень сложно отладить множество потенциальных проблем, которые он вызывает.
Циклический импорт в Python – это форма циклической зависимости, которая создается с помощью оператора импорта в Python.
Например, давайте проанализируем следующий код:
# module1 import module2 def function1(): module2.function2() def function3(): print('Goodbye, World!')
# module2 import module1 def function2(): print('Hello, World!') module1.function3()
# __init__.py import module1 module1.function1()
Когда Python импортирует модуль, он проверяет реестр модулей, чтобы убедиться, что он уже импортирован. Если модуль уже был зарегистрирован, Python использует этот существующий объект из кеша. Реестр модулей – это таблица модулей, которые были инициализированы и проиндексированы по имени модуля. К этой таблице можно получить доступ через sys.modules.
Если он не был зарегистрирован, Python находит модуль, при необходимости инициализирует его и выполняет в пространстве имен нового модуля.
В нашем примере, когда Python достигает import module2, он загружает и выполняет его. Однако module2 также вызывает module1, который, в свою очередь, определяет function1().
Проблема возникает, когда function2() пытается вызвать функцию module1 function3(). Поскольку модуль1 был загружен первым и, в свою очередь, загружен модуль2 до того, как он смог достичь function3(), эта функция еще не определена и выдает ошибку при вызове:
$ python __init__.py Hello, World! Traceback (most recent call last): File "__init__.py", line 3, in <module> module1.function1() File "/Users/scott/projects/sandbox/python/circular-dep-test/module1/__init__.py", line 5, in function1 module2.function2() File "/Users/scott/projects/sandbox/python/circular-dep-test/module2/__init__.py", line 6, in function2 module1.function3() AttributeError: 'module' object has no attribute 'function3'
Как исправить?
Как правило, циклический импорт – это результат плохого дизайна. Более глубокий анализ программы мог бы сделать вывод, что зависимость на самом деле не требуется или что зависимые функции могут быть перемещены в другие модули, которые не будут содержать циклическую ссылку.
Простое решение состоит в том, что иногда оба модуля можно просто объединить в один более крупный. Результирующий код из нашего примера выше будет выглядеть примерно так:
# module 1 2 def function1(): function2() def function2(): print('Hello, World!') function3() def function3(): print('Goodbye, World!') function1()
Однако объединенный модуль может иметь некоторые несвязанные функции (тесная связь) и может стать очень большим, если в двух модулях уже есть много кода.
Так что, если это не сработает, можно было бы отложить импорт module2, чтобы импортировать его только тогда, когда это необходимо. Это можно сделать, поместив импорт module2 в определение function1():
# module 1 def function1(): import module2 module2.function2() def function3(): print('Goodbye, World!')
В этом случае Python сможет загрузить все функции в module1, а затем загрузить module2 только при необходимости.
Этот подход не противоречит синтаксису, поскольку в документации сказано: «Обычно, но не обязательно, помещать все операторы импорта в начало модуля».
В документации также говорится, что рекомендуется использовать import X вместо других операторов, таких как from module import * или from module import a, b, c.
Вы также можете увидеть много кодовых баз, использующих отложенный импорт, даже если нет циклической зависимости, которая ускоряет время запуска, поэтому это вообще не считается плохой практикой (хотя это может быть плохой дизайн, в зависимости от вашего проекта) .
Заключение
Циклический импорт – это особый случай циклических ссылок. Как правило, их можно решить с помощью улучшенного дизайна кода. Однако иногда результирующий дизайн может содержать большой объем кода или смешивать несвязанные функции (жесткая связь).