Dependencia de importación circular en Python [duplicado]

Resuelto Ram Rachum asked hace 15 años • 7 respuestas

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 apaquete __init__.py, el cpaquete se importa. Pero c_file.pylas importaciones a.b.d.

El programa falla y dice bque no existe cuando c_file.pyintenta importar a.b.d. (Y realmente no existe, porque estábamos en medio de importarlo).

¿Cómo se puede solucionar este problema?

Ram Rachum avatar Oct 13 '09 02:10 Ram Rachum
Aceptado

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.

Dirk avatar Oct 12 '2009 19:10 Dirk

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.

Lasse V. Karlsen avatar Oct 12 '2009 19:10 Lasse V. Karlsen

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.Classroomcuando lo necesites.

zachaysan avatar Oct 04 '2013 16:10 zachaysan

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 Vertexclase y una Edgeclase. 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
Christopher Peisert avatar Jun 01 '2020 16:06 Christopher Peisert