Como hacer importacion ciclica

Chema Cortes pych3m4 en gmail.com
Sab Mar 3 03:51:20 CET 2007


El 28/02/07, Juan José Alonso. <kernel.no.found en gmail.com> escribió:
> Saludos, ya me ha ocurrido en varios momentos.. les comento
>
> Tengo un fichero donde tengo una clase Mapa(), y otro fichero donde tengo
> Interfaz()
>
> El tema es que en el fichero de Mapa() necesito importar la clase Interfaz
> y en interfaz necesito importar Mapa() o cualquier otro del mismo fichero
>
> Y la primera importacion va bien, pero al hacer la 2º no me deja, he oido
> algo de que es por ser una importacion ciclica

Es un problema conocido, del que puedes ver una entrada en la FAQ:
  <http://www.python.org/doc/faq/programming.html#id12>

En principio, decir que no debe dar ningún problema si empiezas
importando los módulos tal cuál (mediante el "import modulo"), y que
fallará si intentas importar objetos por nombre (mediante el
"from...import..").

Para comprender mejor lo que pasa, hay que comprender qué pasa cuando
se importa un módulo y el papel del diccionario 'sys.modules'.

PROCESO de importación:

1- Se comprueba si existe una clave en 'sys.modules' con el nombre del
módulo. Si existe, se usa el valor de esa clave para evitar
"reimportar" el módulo. Así terminaría el proceso de importación. (Es
la razón por la que no debe dolernos reimportar un módulo tantas veces
como queramos).

2- Si no existe ninguna clave con el nombre del módulo, se crea en
'sys.modules' una entrada nueva con el nombre del módulo como clave y
se inicializa como módulo vacío (types.ModuleType).

3- Este módulo vacío se utiliza como "espacio de nombres" y se irá
"poblando" de referencias a medida que se ejecute el módulo.

¿Qué pasa si durante la ejecución del módulo se importan otros módulos?

4- Si durante la ejecución del código se importan nuevos módulos,
éstos módulos van a ver al primogénito como ya importado, pero en
realidad todavía estará sin inicializar del todo. Habrá referencias
que todavía no habránn sido creadas ya que iban en orden por detrás de
las importaciones.


SOLUCIONES:

- la solución más simple es importar los módulos completos, evitando
importar objetos sueltos.

- otra solución sería reordenar las referencias para que vayan por
delante de las importaciones (algo que estaría en contra de las reglas
de estilo de programación python)

- la más recomendable: romper las importaciones circulares cambiándo
su alcance de global a local (dentro de una función, método, etc),
dejando como global lo mínimo necesario para inicializar el módulo.


EJEMPLO (del último caso):

Supongamos tu caso, en el que interface tiene que crear un objeto
mapa, y viceversa:

#fichero: interface.py

class Interface:
  def __init__(self):
    from mapa import Mapa
    self.mapa=Mapa()

#fichero: mapa.py

class Mapa:
  def __init__(self):
    from interface import Interface
    self.interface=Interface()

#fichero: prueba.py

from interface import Interface
i=Interface()

En la ejecución del fichero "prueba.py" pasará lo siguiente

1- "from interface import Interface"  declara la clase Interface. No
se importa el módulo Mapa todavía. En sys.modules aparece una entrada
nueva con el módulo Interface como importado.

2- "i=Interface()". Se crea una nueva instancia y se procede a
ejecutar el constructor. El constructor importará el módulo Mapa, el
cuál ya verá al módulo Interface importado y completamente
inicializado.


Espero que quede claro. Conviene crearse algunos ejemplos e ir probándolos.




Más información sobre la lista de distribución Python-es