most likely due to a circular import python как исправить
Круговой импорт Python
Что такое циклическая зависимость?
Циклическая зависимость возникает, когда два или более модуля зависят друг от друга. Это связано с тем, что каждый модуль определяется в терминах другого (см. рис. 1).
Проблемы с циклическими зависимостями
Циклические зависимости могут вызвать довольно много проблем в вашем коде. Например, это может привести к возникновению тесной связи между модулями и, как следствие, к снижению возможности повторного использования кода. Этот факт также делает код более трудным для поддержания в долгосрочной перспективе.
Кроме того, циклические зависимости могут быть источником потенциальных сбоев, таких как бесконечные рекурсии, утечки памяти и каскадные эффекты. Если вы не будете осторожны и у вас есть циклическая зависимость в вашем коде, может быть очень трудно отладить многие потенциальные проблемы, которые она вызывает.
Что такое круговой импорт?
Циклический импорт-это форма циклической зависимости, которая создается с помощью оператора import в Python.
Например, давайте проанализируем следующий код:
Если он не был зарегистрирован, Python находит модуль, инициализирует его при необходимости и выполняет в пространстве имен нового модуля.
Как исправить циклические зависимости
В общем, круговой импорт-это результат плохих замыслов. Более глубокий анализ программы мог бы привести к выводу, что зависимость на самом деле не требуется, или что зависимая функциональность может быть перемещена в другие модули, которые не содержат циклической ссылки.
Простое решение заключается в том, что иногда оба модуля могут быть просто объединены в один, более крупный модуль. Полученный код из нашего примера выше будет выглядеть примерно так:
Однако объединенный модуль может иметь некоторые несвязанные функции (тесная связь) и может стать очень большим, если в двух модулях уже есть много кода.
Поэтому, если это не сработает, другим решением могло бы быть отложить импорт модуля 2, чтобы импортировать его только тогда, когда это необходимо. Это можно сделать, поместив импорт модуля 2 в определение функции 1() :
В этом случае Python сможет загрузить все функции в модуле 1, а затем загрузить модуль 2 только при необходимости.
Этот подход не противоречит синтаксису Python, как говорится в документации Python : “Обычно, но не обязательно размещать все операторы импорта в начале модуля (или скрипта, если уж на то пошло)”.
Вы также можете увидеть много кодовых баз, использующих отложенный импорт, даже если нет циклической зависимости, которая ускоряет время запуска, так что это вообще не считается плохой практикой (хотя это может быть плохой дизайн, в зависимости от вашего проекта).
Сворачивание
Циклический импорт-это особый случай циклических ссылок. Как правило, они могут быть решены с помощью лучшего дизайна кода. Однако иногда результирующая конструкция может содержать большое количество кода или смешивать несвязанные функциональные возможности (тесная связь).
Вы сталкивались с циклическим импортом в своем собственном коде? Если да, то как вы это исправили? Дайте нам знать в комментариях!
Python circular importing?
So I’m getting this error
and you can see that I use the same import statement further up and it works? Is there some unwritten rule about circular importing? How do I use the same class further down the call stack?
7 Answers 7
I think the answer by jpmc26, while by no means wrong, comes down too heavily on circular imports. They can work just fine, if you set them up correctly.
So no, it’s not «working further up in the call stack». This is a stack trace of where the error occurred, which means it errored out trying to import Post in that class. You shouldn’t use circular imports. At best, it has negligible benefit (typically, no benefit), and it causes problems like this. It burdens any developer maintaining it, forcing them to walk on egg shells to avoid breaking it. Refactor your module organization.
To understand circular dependencies, you need to remember that Python is essentially a scripting language. Execution of statements outside methods occurs at compile time. Import statements are executed just like method calls, and to understand them you should think about them like method calls.
When you do an import, what happens depends on whether the file you are importing already exists in the module table. If it does, Python uses whatever is currently in the symbol table. If not, Python begins reading the module file, compiling/executing/importing whatever it finds there. Symbols referenced at compile time are found or not, depending on whether they have been seen, or are yet to be seen by the compiler.
Imagine you have two source files:
Now suppose you compile file X.py. The compiler begins by defining the method X1, and then hits the import statement in X.py. This causes the compiler to pause compilation of X.py and begin compiling Y.py. Shortly thereafter the compiler hits the import statement in Y.py. Since X.py is already in the module table, Python uses the existing incomplete X.py symbol table to satisfy any references requested. Any symbols appearing before the import statement in X.py are now in the symbol table, but any symbols after are not. Since X1 now appears before the import statement, it is successfully imported. Python then resumes compiling Y.py. In doing so it defines Y2 and finishes compiling Y.py. It then resumes compilation of X.py, and finds Y2 in the Y.py symbol table. Compilation eventually completes w/o error.
Something very different happens if you attempt to compile Y.py from the command line. While compiling Y.py, the compiler hits the import statement before it defines Y2. Then it starts compiling X.py. Soon it hits the import statement in X.py that requires Y2. But Y2 is undefined, so the compile fails.
Please note that if you modify X.py to import Y1, the compile will always succeed, no matter which file you compile. However if you modify file Y.py to import symbol X2, neither file will compile.
Any time when module X, or any module imported by X might import the current module, do NOT use:
Any time you think there may be a circular import you should also avoid compile time references to variables in other modules. Consider the innocent looking code:
Suppose module X imports this module before this module imports X. Further suppose Y is defined in X after the import statement. Then Y will not be defined when this module is imported, and you will get a compile error. If this module imports Y first, you can get away with it. But when one of your co-workers innocently changes the order of definitions in a third module, the code will break.
In some cases you can resolve circular dependencies by moving an import statement down below symbol definitions needed by other modules. In the examples above, definitions before the import statement never fail. Definitions after the import statement sometimes fail, depending on the order of compilation. You can even put import statements at the end of a file, so long as none of the imported symbols are needed at compile time.
Note that moving import statements down in a module obscures what you are doing. Compensate for this with a comment at the top of your module something like the following:
In general this is a bad practice, but sometimes it is difficult to avoid.
Циклический импорт в Python
Циклическая зависимость в Python возникает, когда два или более модуля зависят друг от друга. Это связано с тем, что каждый модуль определяется в терминах другого.
Приведенный выше код демонстрирует довольно очевидную циклическую зависимость. functionA() вызывает functionB(), следовательно, в зависимости от него, а functionB() вызывает functionA(). У этого типа циклической зависимости есть некоторые очевидные проблемы, которые мы опишем немного дальше в следующем разделе.
Проблемы с круговыми зависимостями
Циклические зависимости могут вызвать множество проблем в вашем коде. Например, это может привести к тесной связи между модулями и, как следствие, к снижению возможности повторного использования кода. Этот факт также усложняет сопровождение кода в долгосрочной перспективе.
Кроме того, циклические зависимости могут быть источником потенциальных сбоев, таких как бесконечные рекурсии, утечки памяти и каскадные эффекты. Если вы не будете осторожны и у вас есть циклическая зависимость в вашем коде, может быть очень сложно отладить множество потенциальных проблем, которые он вызывает.
Что такое циклический импорт в Python?
Циклический импорт в Python – это форма циклической зависимости, которая создается с помощью оператора импорта в Python.
Например, давайте проанализируем следующий код:
Когда Python импортирует модуль, он проверяет реестр модулей, чтобы убедиться, что он уже импортирован. Если модуль уже был зарегистрирован, Python использует этот существующий объект из кеша. Реестр модулей – это таблица модулей, которые были инициализированы и проиндексированы по имени модуля. К этой таблице можно получить доступ через sys.modules.
Если он не был зарегистрирован, Python находит модуль, при необходимости инициализирует его и выполняет в пространстве имен нового модуля.
В нашем примере, когда Python достигает import module2, он загружает и выполняет его. Однако module2 также вызывает module1, который, в свою очередь, определяет function1().
Проблема возникает, когда function2() пытается вызвать функцию module1 function3(). Поскольку модуль1 был загружен первым и, в свою очередь, загружен модуль2 до того, как он смог достичь function3(), эта функция еще не определена и выдает ошибку при вызове:
Как исправить?
Как правило, циклический импорт – это результат плохого дизайна. Более глубокий анализ программы мог бы сделать вывод, что зависимость на самом деле не требуется или что зависимые функции могут быть перемещены в другие модули, которые не будут содержать циклическую ссылку.
Простое решение состоит в том, что иногда оба модуля можно просто объединить в один более крупный. Результирующий код из нашего примера выше будет выглядеть примерно так:
Однако объединенный модуль может иметь некоторые несвязанные функции (тесная связь) и может стать очень большим, если в двух модулях уже есть много кода.
Так что, если это не сработает, можно было бы отложить импорт module2, чтобы импортировать его только тогда, когда это необходимо. Это можно сделать, поместив импорт module2 в определение function1():
В этом случае Python сможет загрузить все функции в module1, а затем загрузить module2 только при необходимости.
Этот подход не противоречит синтаксису, поскольку в документации сказано: «Обычно, но не обязательно, помещать все операторы импорта в начало модуля».
В документации также говорится, что рекомендуется использовать import X вместо других операторов, таких как from module import * или from module import a, b, c.
Заключение
Циклический импорт – это особый случай циклических ссылок. Как правило, их можно решить с помощью улучшенного дизайна кода. Однако иногда результирующий дизайн может содержать большой объем кода или смешивать несвязанные функции (жесткая связь).
Python ImportError (most likely due to a circular import) for pip install but not local setup.py
I received a Github issue from a colleague trying to use the package. Basically, doing pip install pliffy installs the package, but when you start a Python interpreter and try to import the package you get the follow error message:
I have been able to reproduce the issue on my own machine after creating a fresh Python environment and pip-intalling my package (yes, I now know the importance of testing your own packages after you push them to Pypi; won’t make that mistake again!)
I have run into trouble with circular imports when using type-hinting my own classes/objects, but I thought I solved everything given that I was able to install and use my package locally.
In case it would be useful, here is how I create my wheel and push my package to PyPi:
Any help with this issue would be greatly appreciated.
Update: Additional info
Dear Justin, thank you for taking the time to read through my question and asking for more information.
In response to your question, I create a local environment, locally installed my package (i.e. pliffy), and started up a Python interpreter not in the package folder. I gott an error message when I tried to import my package, but a different error this time:
This tells me that previously I was not getting an error because I was starting the Python interpreter when I was in the package directory. Why I get an error (and a different one at that) when I start the Python interpreter not in the package folder is not clear to me.
Dustin, you also asked about the import structure. I am not too sure how much (little) info, but I decided to provide eveything:
init.py [Only bottom two import are needed for API, but other imports were needed to be able to get my test suite to work (this may not be needed, but when I coded this, I could not tests these other modules unless I included explicite imports here).]
demo.py
estimate.py
parser.py
plot.py
utils.py
figure/init.py
Круговой (или циклический) импорт в Python
Что произойдет, если два модуля импортируют друг друга?
чтобы обобщить проблему, как насчет циклического импорта в Python?
9 ответов:
была действительно хорошая дискуссия по этому поводу в комп.ленг.питон в прошлом году. Он отвечает на ваш вопрос довольно тщательно.
импорт довольно прост на самом деле. Просто помните следующее:
‘и’ от ХХХ импорт ыыы являются исполняемых операторов. Они исполняют когда запущенная программа достигает этой строки.
Если модуль не в sys.модули, затем импорт создает новый модуль вход в систему системный.модули и затем выполняет код в модуле. Это не возвращайте управление вызывающему модулю до завершения выполнения.
Если модуль существует в sys.модули затем импорт просто возвращает это модуль независимо от того, завершил ли он выполнение. Это причина, почему циклический импорт может возвращать модули, которые кажутся частично пустыми.
наконец, выполняемый скрипт выполняется в модуле с именем _ _ main__, импортируя скрипт под свой собственный имя создаст новый модуль, не связанный с __главный.__
возьмите эту партию вместе, и вы не должны получать никаких сюрпризов при импорте модули.
циклический импорт завершается, но вы должны быть осторожны, чтобы не использовать циклически импортированные модули во время инициализации модуля.
рассмотрим следующие файлы:
если вы выполняете a.py, вы получите следующее:
о втором импорте b.py (во втором a in ), интерпретатор Python не импортирует b опять же, потому что он уже существует в модуль дикт.
добавьте следующую строку в a.py :
поскольку другие ответы описывают этот шаблон, он приемлем в python:
что позволит избежать выполнения инструкции import при импорте файла другими модулями. Только если существует логическая циклическая зависимость, это не удастся.
большинство круговых импорта на самом деле не являются логическим круговым импортом, а скорее повышают ImportError ошибки, так как import() оценивает операторы верхнего уровня всего файла, когда называемый.
эти ImportErrors почти всегда можно избежать, если вы положительно хотите, чтобы ваш импорта на вершине:
рассмотрим этот круговой импорт:
Приложение
Приложение Б
это пытается импортировать SimplifiedImageSerializer и если ImportError поднимается, потому что он уже импортирован, он будет тянуть его из importcache.
PS: вы должны прочитать весь этот пост в голосе Дэвида Бизли.
у меня есть пример, который поразил меня!
foo.py
bar.py
main.py
Я полностью согласен с ответом pythoneer здесь. Но я наткнулся на некоторый код, который был испорчен циклическим импортом и вызвал проблемы при попытке добавить модульные тесты. Поэтому, чтобы быстро исправить его, не меняя все, что вы можете решить проблему, выполнив динамический импорт.
опять же, это не постоянное исправление, но может помочь кому-то, кто хочет исправить ошибку импорта, не меняя слишком много кода.
модуль b.py
запуск «модуля a» выведет:
он выводил эти 3 строки, в то время как он должен был выводить infinitival из-за кругового импорта. Что происходит строка за строкой при запуске «модуля a», указано здесь:
круговой импорт может быть запутанным, потому что импорт делает две вещи:
первый делается только один раз, в то время как последний в каждом операторе импорта. Циклический импорт создает ситуацию, когда при импорте модуля используется импортированный модуль с частично выполненным кодом. В следствии он не будет видеть объекты, созданные после импорта. Ниже код пример демонстрирует это.
круговой импорт не является окончательным злом, которого следует избегать любой ценой. В некоторых фреймворках, таких как Flask, они вполне естественны, и настройка вашего кода для их устранения не делает код лучше.
python main.py вывод с комментариями