Dependencia de importación circular en Python [duplicado]
Digamos que tengo la siguiente estructura de directorios:
a\
__init__.py
b\
__init__.py
c\
__init__.py
c_file.py
d\
__init__.py
d_file.py
En el a
paquete __init__.py
, el c
paquete se importa. Pero c_file.py
las importaciones a.b.d
.
El programa falla y dice b
que no existe cuando c_file.py
intenta importar a.b.d
. (Y realmente no existe, porque estábamos en medio de importarlo).
¿Cómo se puede solucionar este problema?
Puede aplazar la importación, por ejemplo en a/__init__.py
:
def my_function():
from a.b.c import Blah
return Blah()
es decir, diferir la importación hasta que sea realmente necesaria. Sin embargo, también examinaría de cerca las definiciones/usos de mi paquete, ya que una dependencia cíclica como la señalada podría indicar un problema de diseño.
Si a depende de c y c depende de a, ¿no son en realidad la misma unidad?
Realmente deberías examinar por qué has dividido a y c en dos paquetes, porque o tienes algún código que deberías dividir en otro paquete (para que ambos dependan de ese nuevo paquete, pero no entre sí), o deberías fusionarlos. en un solo paquete.
Me he preguntado esto un par de veces (generalmente mientras trato con modelos que necesitan conocerse entre sí). La solución simple es simplemente importar el módulo completo y luego hacer referencia a lo que necesita.
Entonces en lugar de hacer
from models import Student
en uno, y
from models import Classroom
en el otro, solo hazlo
import models
en uno de ellos, luego llama models.Classroom
cuando lo necesites.
Dependencias circulares debido a sugerencias de tipo
Con sugerencias de tipo, hay más oportunidades para crear importaciones circulares. Afortunadamente, existe una solución utilizando la constante especial: typing.TYPE_CHECKING
.
El siguiente ejemplo define una Vertex
clase y una Edge
clase. Una arista está definida por dos vértices y un vértice mantiene una lista de las aristas adyacentes a las que pertenece.
Sin sugerencias de tipo, sin errores
Archivo: vértice.py
class Vertex:
def __init__(self, label):
self.label = label
self.adjacency_list = []
Archivo: borde.py
class Edge:
def __init__(self, v1, v2):
self.v1 = v1
self.v2 = v2
Tipo Sugerencias CausaImportError
ImportError: no se puede importar el nombre 'Edge' del módulo 'edge' parcialmente inicializado (probablemente debido a una importación circular)
Archivo: vértice.py
from typing import List
from edge import Edge
class Vertex:
def __init__(self, label: str):
self.label = label
self.adjacency_list: List[Edge] = []
Archivo: borde.py
from vertex import Vertex
class Edge:
def __init__(self, v1: Vertex, v2: Vertex):
self.v1 = v1
self.v2 = v2
Solución usando TYPE_CHECKING
Archivo: vértice.py
from typing import List, TYPE_CHECKING
if TYPE_CHECKING:
from edge import Edge
class Vertex:
def __init__(self, label: str):
self.label = label
self.adjacency_list: List[Edge] = []
Archivo: borde.py
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from vertex import Vertex
class Edge:
def __init__(self, v1: Vertex, v2: Vertex):
self.v1 = v1
self.v2 = v2
Observe la ausencia de comillas alrededor de las sugerencias tipográficas. Debido a la evaluación pospuesta de las anotaciones en Python 3.10 , estas sugerencias de tipo se tratan como si estuvieran entre comillas.
Sugerencias de tipo entre comillas y sin comillas
En las versiones de Python anteriores a la 3.10, los tipos importados condicionalmente deben estar entre comillas, convirtiéndolos en "referencias directas", lo que los oculta del tiempo de ejecución del intérprete.
En Python 3.7, 3.8 y 3.9, una solución alternativa es utilizar la siguiente importación especial.
from __future__ import annotations