Ошибка циклического импорта
Подумайте над своим кодом и преобразуйте его так, чтобы не нужно было из ваших модулей импортировать друг друга. Разбейте ваш код ещё на отдельные модули, например.
12 дек 2022 в 5:23
@CrazyElf и правда, спасибо.
12 дек 2022 в 9:05
0
Сортировка: Сброс на вариант по умолчанию
Знаете кого-то, кто может ответить? Поделитесь ссылкой на этот вопрос по почте, через Твиттер или Facebook.
- python
- telebot
- python-import
-
Важное на Мете
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.10.27.43697
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Avoiding Circular Imports in Python
If you’ve written enough Python code, you’ve almost certainly found yourself staring down a stack trace that that ends with:
ImportError: cannot import name '' from partially initialized module '' (most likely due to a circular import).
In my experience, running into such an error usually prompts a long and tedious inspection of the codebase. The end result is almost always some sort of refactor–ranging from small adjustments of imports all the way to large changes in a project’s directory structures.
The reason why circular imports in Python result in errors has been explained by others much more eloquently than I could manage — instead of focusing on the reason behind the problem, in this post we will provide some practical advice that might help you avoid circular imports in large Python projects (or at least make them easier to debug!).
Not all Circular Imports are Created Equal
Some circular imports in Python are “real”, and cannot be resolved without moving where your objects are defined. An example of this can be seen in this repository. If you clone the repository locally, check out the circular-imports-require-refactor branch and try to run main.py , you’ll see the import error show up:
$ python3 ./main.py
Traceback (most recent call last):
File "/Users/andremenck/repos/personal/circular_import_examples/./main.py", line 1, in
from child import Child
File "/Users/andremenck/repos/personal/circular_import_examples/child.py", line 4, in
from parent import Person
File "/Users/andremenck/repos/personal/circular_import_examples/parent.py", line 4, in
from child import Child
ImportError: cannot import name 'Child' from partially initialized module 'child' (most likely due to a circular import) (/Users/andremenck/repos/personal/circular_import_examples/child.py)
So, what is going on here? Well, this case is (deliberately) pretty simple:
- The Child class in the child file depends on the Person class in the parent module
- The Parent class in the parent file depends on the Child class in the child module
In this contrived example, the solution is really quite simple: move the Person class definition into a new file, and have child and parent pull from that. The notable thing here is that in order to resolve this circular import, we actually had to move a class definition into another file (the solution can be seen in another branch in the same repository).
Importing from Parent Modules
If you asked me a couple of weeks ago, I would have said that pretty much all circular imports would require such refactors (ie: changing where objects are defined) — of course, had I been correct, this would be a much less interesting blog post. In fact, as our Python libraries at Brex grow and become more complex, we started to notice that many circular imports had a very different root cause: they were happening because we were importing objects from the wrong place, not because we were defining objects in the wrong place. Rather than me fumbling for a description, let’s consider an example. Take the following directory structure and files (this is replicated in another branch of our repo):
./
├── main.py
└── objects
├── __init__.py
├── child.py
├── parent.py
└── person.py
This is exactly the same directory structure we had in the “fixed” branch for our imports. However, we have added some slight changes:
- We added imports for all of our classes in the objects/__init__.py file
- The imports in person.py and child.py were changed to import from objects , instead of importing from each submodule
In other words, we have:
Yes, this is the after graph! I attempted to generate a before graph to show that this is actually a lot simpler than our previous situation… but about 2h of waiting, pydeps crashed and refused to produce anything usable. I guess that alone should tell you something about the previous state, and how using direct imports improved our codebase’s sanity!
Python Circular Import Problem and Solutions
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 –
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 –
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 –
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 –
from module2 import func2 def func1(): func2()
from module1 import func1 def func2(): func1()
python .\module1.py Traceback (most recent call last): File "/home/pythonpool/module1.py", line 1, in from module2 import func2 File "/home/pythonpool/module2.py", line 1, in from module1 import func1 File "/home/pythonpool/module1.py", line 1, in 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.
Popular now
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 –
from django.db.models import get_model MyModel = get_model('app1', 'App1')
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 –
from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__ name __) db = SQLAlchemy(app) from models import routes
from app import db
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.
Python Error: “most likely due to a circular import” (Solution)
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.
Circular Import Error
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:\Users\CodersLegacy\random.py", line 1, in import random File "c:\Users\CodersLegacy\random.py", line 3, in 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.
Share on Facebook
Follow us