Saltar al contenido

¿Cómo evitar las importaciones circulares en Python?

Si encuentras alguna parte que no comprendes puedes dejarlo en la sección de comentarios y te ayudaremos tan rápido como podamos.

Solución:

Considere el siguiente paquete de Python de ejemplo donde a.py y b.py dependen unos de otros:

/package
    __init__.py
    a.py
    b.py

Tipos de problemas de importación circular

Las dependencias de importación circular generalmente se dividen en dos categorías según lo que intente importar y dónde lo use dentro de cada módulo. (Y si está usando Python 2 o 3).

1. Errores al importar módulos con importaciones circulares

En algunos casos, simplemente importador un módulo con una dependencia de importación circular puede generar errores incluso si no hace referencia a nada del módulo importado.

Hay varias formas estándar de importar un módulo en python

import package.a           # (1) Absolute import
import package.a as a_mod  # (2) Absolute import bound to different name
from package import a      # (3) Alternate absolute import
import a                   # (4) Implicit relative import (deprecated, python 2 only)
from . import a            # (5) Explicit relative import

Desafortunadamente, solo las opciones 1 y 4 funcionan cuando tiene dependencias circulares (el resto genera ImportError
o AttributeError). En general, no debería usar la cuarta sintaxis, ya que solo funciona en python2 y corre el riesgo de entrar en conflicto con otros módulos de terceros. Entonces, realmente, solo se garantiza que funcione la primera sintaxis.

EDITAR: El ImportError y AttributeError los problemas solo ocurren en python 2. En python 3, la maquinaria de importación se ha reescrito y todas estas declaraciones de importación (con la excepción de 4) funcionarán, incluso con dependencias circulares. Si bien las soluciones de esta sección pueden ayudar a refactorizar el código de Python 3, están destinadas principalmente a las personas que usan Python 2.

Importación absoluta

Simplemente use la primera sintaxis de importación anterior. La desventaja de este método es que los nombres de importación pueden obtener súper largo para paquetes grandes.

En a.py

import package.b

En b.py

import package.a

Aplazar la importación hasta más tarde

He visto este método utilizado en muchos paquetes, pero todavía me parece raro, y no me gusta que no pueda mirar en la parte superior de un módulo y ver todas sus dependencias, tengo que buscar en todas las funciones. así como.

En a.py

def func():
    from package import b

En b.py

def func():
    from package import a

Ponga todas las importaciones en un módulo central

Esto también funciona, pero tiene el mismo problema que el primer método, donde todas las llamadas de paquetes y submódulos se obtienen súper largo. También tiene dos defectos importantes: obliga a todos los submódulos para ser importado, incluso si solo está usando uno o dos, y todavía no puede mirar ninguno de los submódulos y ver rápidamente sus dependencias en la parte superior, tiene que examinar las funciones.

En __init__.py

from . import a
from . import b

En a.py

import package

def func():
    package.b.some_object()

En b.py

import package

def func():
    package.a.some_object()

2. Errores al usar objetos importados con dependencias circulares

Ahora, si bien es posible que pueda importar un módulo con una dependencia de importación circular, no podrá importar ningún objeto definido en el módulo ni podrá hacer referencia a ese módulo importado en ninguna parte del nivel superior del módulo donde lo está importando. Sin embargo, puede utilizar el módulo importado en el interior funciones y bloques de código que no se ejecutan en la importación.

Por ejemplo, esto funcionará:

paquete/a.py

import package.b

def func_a():
    return "a"

paquete/b.py

import package.a

def func_b():
    # Notice how package.a is only referenced *inside* a function
    # and not the top level of the module.
    return package.a.func_a() + "b"

Pero esto no funcionará

paquete/a.py

import package.b

class A(object):
    pass

paquete/b.py

import package.a

# package.a is referenced at the top level of the module
class B(package.a.A):
    pass

obtendrá una excepción

AttributeError: el módulo ‘paquete’ no tiene attribute ‘a’

Generalmente, en la mayoría de los casos válidos de dependencias circulares, es posible refactorizar o reorganizar el código para evitar estos errores y mover las referencias del módulo dentro de un bloque de código.

Solo importe el módulo, no importe desde el módulo:

Considerar a.py:

import b

class A:
    def bar(self):
        return b.B()

y b.py:

import a

class B:
    def bar(self):
        return a.A()

Esto funciona perfectamente bien.

Hacemos una combinación de funciones e importaciones absolutas para una mejor lectura y cadenas de acceso más cortas.

  • Ventaja: cadenas de acceso más cortas en comparación con las importaciones absolutas puras
  • Desventaja: un poco más de sobrecarga debido a la llamada de función adicional

principal/sub/a.py

import main.sub.b
b_mod = lambda: main.sub.b

class A():
    def __init__(self):
        print('in class "A":', b_mod().B.__name__)

principal/sub/b.py

import main.sub.a
a_mod = lambda: main.sub.a

class B():
    def __init__(self):
        print('in class "B":', a_mod().A.__name__)

Acuérdate de que tienes la opción de añadir una evaluación justa .

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *