Importación circular de Python
- Reproducir error de importación circular en Python
- Resolver error de importación circular en Python
- Resuelva el error de importación circular causado por la sugerencia de tipo en Python
En este artículo, aprenderemos cómo surgen comúnmente las importaciones circulares y las principales formas de solucionarlas. También veremos cómo corregir un error de importación circular causado por una sugerencia de tipo en python.
Reproducir error de importación circular en Python
¿Alguna vez te has encontrado con un error al intentar importar un módulo? En este caso, obtenemos un error que se muestra a continuación.
from My_Module_A import My_FUNC_A
def My_main_Func():
My_FUNC_A()
if __name__ == "main":
My_main_Func()
Producción :
ImportError: cannot import name 'My_FUNC_A' from partially initialized module 'My_Module_A' (most likely due to a circular import)
Nos dice que la causa más probable es una importación circular. Esta es la forma más simple y común en la que este tipo de ciclo de importación puede ocurrir en tiempo de ejecución. Main intentó importar algo de My_Module_A
.
Entonces, comienza a inicializar My_Module_A
y ejecuta el código desde My_Module_A
, pero la primera línea en My_Module_A
es para importar algo del módulo MY_Module_B
.
Por lo tanto, deja de inicializar el módulo My_Module_A
porque MY_Module_B
debe inicializarse primero. Y luego, salta a MY_Module_B
y comienza a ejecutar ese código en su lugar.
Pero, la primera línea en el módulo MY_Module_B
necesita algo de My_Module_A
, por lo que deja de ejecutar MY_Module_B
y pasa a My_Module_A
.
Y le gustaría comenzar a inicializar My_Module_A
, pero se da cuenta de que My_Module_A
ya está en el proceso de inicialización. Entonces ahí es donde ocurre el error.
Podemos ver cómo se desarrolla esta cadena de eventos en el rastreo. Aquí vemos en main.py
; intenta importar la función My_FUNC_A
del módulo My_Module_A
, lo que provocó la importación de la función MY_Func_B2
del módulo MY_Module_B
.
Y, MY_Module_B.py
activó la importación de la función My_FUNC_A
nuevamente desde el módulo My_Module_A
, donde ocurrió el error final.
Traceback (most recent call last):
File "c:\Users\Dell\Desktop\demo\main.py", line 1, in <module>
from My_Module_A import My_FUNC_A
File "c:\Users\Dell\Desktop\demo\My_Module_A.py", line 1, in <module>
from MY_Module_B, import MY_Func_B2
File "c:\Users\Dell\Desktop\demo\MY_Module_B.py", line 1, in <module>
from My_Module_A import My_FUNC_A
ImportError: cannot import name 'My_FUNC_A' from partially initialized module 'My_Module_A' (most likely due to a circular import) (c:\Users\Dell\Desktop\demo\My_Module_A.py)
Tenemos un ciclo de tiempo de importación entre los módulos My_Module_A
y MY_Module_B
, pero si observa los nombres que importamos, no los usamos en el momento de la importación.
Los usamos cuando se llama a esta función My_FUNC_A()
o cuando se llama a esta función MY_Func_B2()
. Dado que no estamos llamando a estas funciones en el momento de la importación, no existe una verdadera dependencia cíclica, lo que significa que podemos eliminar este ciclo de importación.
Resolver error de importación circular en Python
Hay tres formas comunes de resolver este problema; la primera es tomar su importación y ponerla dentro de la función.
En Python, las importaciones pueden ocurrir en cualquier momento, incluso cuando se llama a una función; si necesita esta función MY_Func_B1()
, entonces podría ser la importación dentro de la función My_FUNC_A()
.
def My_FUNC_A():
from MY_Module_B import MY_Func_B1
MY_Func_B1()
Podría ser aún más eficiente porque si nunca llama a la función My_FUNC_A()
, entonces puede ser que nunca tenga que importar MY_Func_B1
en absoluto. Por ejemplo, supongamos que tiene un montón de funciones diferentes en My_Module_A
, y muchas diferentes usan esta función MY_Func_B1()
.
En ese caso, la mejor solución sería importar el módulo MY_Module_B
directamente y en las funciones donde lo use, use el nombre más largo como MY_Module_B.MY_Func_B1()
. Así que continúe y haga esta conversión para todos los módulos involucrados en el ciclo.
import MY_Module_B
def My_FUNC_A():
MY_Module_B.MY_Func_B1()
Veamos cómo esto resuelve el ciclo de importación en el error de tiempo de ejecución. Primero, nuestra función My_main_Func()
intenta importar algo de My_Module_A
, lo que hace que My_Module_A
comience a ejecutarse. Luego, en My_Module_A.py
, podemos importar el módulo MY_Module_B
, que activa MY_Module_B
para comenzar a ejecutarse.
En MY_Module_B.py
, cuando llegamos al módulo de importación My_Module_A
, el módulo ya ha comenzado a inicializarse; este objeto de módulo técnicamente existe. Dado que existe, no comienza a volver a ejecutar esto.
Simplemente dice, está bien, eso existe, por lo que el módulo MY_Module_B
continuará, finalizará su importación y luego, una vez finalizada la importación, el módulo My_Module_A
terminará de importarse.
Producción :
15
La tercera y última forma común de eliminar este ciclo de importación es fusionar los dos módulos. Entonces, nuevamente, sin importaciones, sin ciclos de importación; sin embargo, si tenía dos o incluso más módulos en primer lugar, probablemente los tenía por una razón.
No solo vamos a recomendar que tome todos sus módulos y los fusione en uno; eso sería una tontería. Por lo tanto, probablemente debería preferir usar uno de los dos primeros métodos.
Código fuente del archivo My_Module_A.py
.
import MY_Module_B
def My_FUNC_A():
print(MY_Module_B.MY_Func_B1())
Código fuente del archivo MY_Module_B.py
.
import My_Module_A
def MY_Func_B1():
return 5 + 10
def MY_Func_B2():
My_Module_A.My_FUNC_A()
Código fuente del archivo main.py
.
from My_Module_A import My_FUNC_A
def My_main_Func():
My_FUNC_A()
if __name__ == "main":
My_main_Func()
Resuelva el error de importación circular causado por la sugerencia de tipo en Python
Veamos otro ejemplo, el siguiente tipo de ciclo de importación más común que ejecutará debido al uso de sugerencias de tipo. Este ejemplo es diferente al anterior porque las anotaciones de tipo se definen en una función o definición de clase.
Cuando la función está definida, o la clase está definida pero no definida cuando la ejecutamos, el caso de uso más común de sugerencia de tipo no es ni siquiera en tiempo de ejecución. Si todo lo que nos importa es el análisis estático, entonces ni siquiera necesitamos hacer la importación en tiempo de ejecución.
El módulo escribiendo
proporciona esta variable, TYPE_CHECKING
es una constante falsa en tiempo de ejecución. Entonces, si ponemos todas las importaciones que necesitamos para la verificación de tipos en uno de estos, entonces no ocurrirá nada en tiempo de ejecución.
Evitando el bucle de importación por completo, si importa esta clase Mod_A_class
, obtendrá un error de nombre porque este nombre Mod_A_class
no se ha importado.
Producción :
Traceback (most recent call last):
File "c:\Users\Dell\Desktop\demo\main.py", line 1, in <module>
from My_Module_A import My_FUNC_A
File "c:\Users\Dell\Desktop\demo\My_Module_A.py", line 9, in <module>
from MY_Module_B, import MY_Func_B1
File "c:\Users\Dell\Desktop\demo\MY_Module_B.py", line 22, in <module>
class Mod_b_class:
File "c:\Users\Dell\Desktop\demo\MY_Module_B.py", line 23, in Mod_b_class
def __init__(self,a : Mod_A_class):
NameError: name 'Mod_A_class' is not defined
Para arreglar esto, agregue esto desde __future__
import annotations
. ¿Que hace?
Cambia la forma en que funcionan las anotaciones; en lugar de evaluar Mod_b_class
como nombre, todas las anotaciones se convierten en cadenas.
Así que ahora no recibe un error en tiempo de ejecución aquí porque aunque el nombre Mod_b_class
no se importa, la cadena Mod_b_class
ciertamente existe. Sin embargo, debe tener en cuenta que cada vez que realiza uno de estos desde las importaciones __future__
, no es necesariamente un comportamiento garantizado para siempre.
Ahora, esto hace que sus anotaciones se manejen como cadenas, pero existe una propuesta que compite para que solo se evalúen de forma perezosa. Por lo tanto, no se evalúan a menos que intente mirarlos e inspeccionar sus sugerencias de tipo en tiempo de ejecución.
Esto probablemente no hará ninguna diferencia para su base de código, pero aún debe tenerlo en cuenta. Una solución alternativa es volver a utilizar importar módulos
en lugar de desde importaciones de módulos
, y aún necesitará las anotaciones
de __futuro__
.
Código fuente completo del módulo My_Module_A.py
.
# from __future__ import annotations
# from typing import TYPE_CHECKING
# from MY_Module_B import MY_Func_B1
# if TYPE_CHECKING:
# from MY_Module_B import Mod_b_class
# def My_FUNC_A():
# b : Mod_b_class = MY_Func_B1()
# class Mod_A_class:
# def __init__(self,b : Mod_b_class):
# self.b=b
from __future__ import annotations
import MY_Module_B
def My_FUNC_A():
b: MY_Module_B.Mod_b_class = MY_Module_B.MY_Func_B1()
class Mod_A_class:
def __init__(self, b: MY_Module_B.Mod_b_class):
self.b = b
Código fuente completo del módulo MY_Module_B.py
.
from __future__ import annotations
import My_Module_A
def MY_Func_B1():
...
class Mod_b_class:
def __init__(self, a: My_Module_A.Mod_A_class):
self.a = a
Código fuente completo del archivo main.py
.
from My_Module_A import My_FUNC_A
def My_main_Func():
My_FUNC_A()
if __name__ == "main":
My_main_Func()
Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.
LinkedIn