Python Circular Imports Module: Solving Circular Import Problem
In python, a module can be made by importing other modules. In some cases, a Circular dependency is created. Circular dependency is the case when some modules depend on each other. It can create problems in the script such as tight coupling, and potential failure.
To run the script without any error the Circular dependency has to be removed. Circular dependency can be created in many ways. With a well-designed program, these cases can be avoided.
Python Circular Imports:
Circular importing is a conceptual error that is formed due to circular dependency created with the import statement in our Python program.
- Python Circular Imports is a type of Circular dependency. It occurs in python when two or more models import each other and it repeats the importing connection into an infinite circular call.
- With Circular Imports, the python script gives an error. To run the python script it has to be removed and it is very difficult to find and remove the script manually.
- Circular imports are created because of the bad coding design and implementation-related logical anomalies.
Here is a situation shown using three separate programs:
# module1 created import module2 def function1(): module2.function2() def function3(): print('Hasta la vista, Gaurav!') # module 2 import module1 def function2(): print('Hey, Gaurav') module1.function3() # __init.py import module1 module1.function1()
Explanation:
Here, we have defined three programs. 2 of them are treated as modules. In the first code function1() has been called that has a module2.function2() within itself as the calling function. The function2() of module 2 again has a print() and function3 and the body of function3() has some print().
Finally, the __init__.py is calling module1.function1() . This circular calls of importing statements and their associated function will create an error.
When Python performs the importing of a module, it verifies and goes through the module registry to identify if the module was already imported. In case the module has already been there registered, Python uses its existing object from cache.
The module registry is nothing but a data structure or tabular structure containing information about multiple imports (predefined and user defined) of modules that were initialized and indexed with module name(s) . Developers can access this table using sys.modules .
Fixing Circular Imports in python
There are many ways by which Circular imports can be avoided. Some of them are:
- Change Name of Working python script
- Import the module
- Avoid Circular Import
- Merge modules
- Import when need
Change Name of working python script:
Changing the name of the Working file different from the module which is imported in the script can avoid the Circular Imports problem.
Import the module:
Avoid importing objects or functions from a module that can cause Circular Imports. It is good to import the whole module to avoid the Circular Import.
Avoid Circular Import:
There are many cases where one module function depends on another model which in turn depends on it. This case mostly creates Circular Imports.
Merge modules:
When one module depends on another model and that module depends on first then it is good practice to Merge both the modules to avoid Circular Imports.
# one module def fun1(): print('In function 1') fun2() def fun2(): print('In function2') function3() def function3(): print('In function 3') fun1()
Explanation:
Here, merge both module1 and module2, so that the user-defined functions within them come under one module.
Import when need:
The python module can be imported anywhere in the program. It is not necessary for python to first import the module and start working over it. Importing the module before one line where it is needed to avoid Circular Import.
def fun1(): print('In function 1') import newmodule newmodule.fun3() def fun2(): print('In function 2')
Explanation:
Here, In function one imports the newmodule before calling the fun3.
We hope this article has given a crisp idea on how to stay ahead of circular import issue. We learn about Circular dependency, Circular Imports, and various ways to solve the problem of Circular imports. Circular Imports reduce code reusability and create infinite recursions leading to inefficient programming and memory leaks, and can even lead to cascade effects. It is good programming practice to avoid Circular Imports.
- Learn Python Programming
- Addition of two numbers in Python
- TypeError: ‘int’ object is not subscriptable
- Python Factorial
- Python Uppercase
- Top Online Python Compiler
- Inheritance in Python
- Python : end parameter in print()
- Python New 3.6 Features
- Python eval
- Python IDE
- Install Opencv Python PIP Windows
- String Index Out of Range Python
- Reverse Words in a String Python
- Only Size-1 Arrays Can be Converted to Python Scalars
- Attribute Error Python
- Python list append and extend
- Python KeyError
- Python Return Outside Function
- Pangram Program in Python
Циклический импорт в Python
Циклическая зависимость в Python возникает, когда два или более модуля зависят друг от друга. Это связано с тем, что каждый модуль определяется в терминах другого.
Приведенный выше код демонстрирует довольно очевидную циклическую зависимость. functionA() вызывает functionB(), следовательно, в зависимости от него, а functionB() вызывает functionA(). У этого типа циклической зависимости есть некоторые очевидные проблемы, которые мы опишем немного дальше в следующем разделе.
Проблемы с круговыми зависимостями
Циклические зависимости могут вызвать множество проблем в вашем коде. Например, это может привести к тесной связи между модулями и, как следствие, к снижению возможности повторного использования кода. Этот факт также усложняет сопровождение кода в долгосрочной перспективе.
Кроме того, циклические зависимости могут быть источником потенциальных сбоев, таких как бесконечные рекурсии, утечки памяти и каскадные эффекты. Если вы не будете осторожны и у вас есть циклическая зависимость в вашем коде, может быть очень сложно отладить множество потенциальных проблем, которые он вызывает.
Что такое циклический импорт в Python?
Циклический импорт в 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 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.
Вы также можете увидеть много кодовых баз, использующих отложенный импорт, даже если нет циклической зависимости, которая ускоряет время запуска, поэтому это вообще не считается плохой практикой (хотя это может быть плохой дизайн, в зависимости от вашего проекта) .
Заключение
Циклический импорт – это особый случай циклических ссылок. Как правило, их можно решить с помощью улучшенного дизайна кода. Однако иногда результирующий дизайн может содержать большой объем кода или смешивать несвязанные функции (жесткая связь).