2010-03-17 17 views
7

Antes que nada: sí sé que ya hay muchas preguntas y respuestas sobre el tema de las importaciones circulares.Python: se necesitan importaciones circulares para la verificación de tipos

La respuesta es más o menos: "Diseña la estructura de tu Módulo/Clase correctamente y no necesitarás importaciones circulares". Eso es verdad. Traté muy duro de hacer un diseño adecuado para mi proyecto actual, en mi opinión, tuve éxito con esto.

Pero mi problema específico es el siguiente: Necesito una comprobación de tipo en un módulo que ya ha sido importado por el módulo que contiene la clase para comprobar. Pero esto arroja un error de importación.

así:

foo.py:

from bar import Bar 

class Foo(object): 

    def __init__(self): 
     self.__bar = Bar(self) 

bar.py:

from foo import Foo 

class Bar(object): 

    def __init__(self, arg_instance_of_foo): 
     if not isinstance(arg_instance_of_foo, Foo): 
      raise TypeError() 

Solución 1: Si lo modifico para comprobar el tipo de una comparación de cadenas , funcionará. Pero realmente no me gusta esta solución (la comparación de cadenas es bastante costosa para una verificación de tipo simple, y podría generar un problema cuando se trata de refactorizar).

bar_modified.py:

from foo import Foo 

class Bar(object): 

    def __init__(self, arg_instance_of_foo): 
     if not arg_instance_of_foo.__class__.__name__ == "Foo": 
      raise TypeError() 

Solución 2: También podría empacar las dos clases en un solo módulo. Pero mi proyecto tiene muchas clases diferentes, como el ejemplo "Barra", y quiero separarlas en diferentes archivos de módulos.

Después de mis propias 2 soluciones no son una opción para mí: ¿Alguien tiene una mejor solución para este problema?

+5

Cómo sobre el uso escribiendo pato? –

+0

Consulte https://stackoverflow.com/questions/39740632/python-type-hinting-without-cyclic-imports para obtener la respuesta correcta. –

Respuesta

4

La mejor solución es no verificar los tipos.

La otra solución es no crear una instancia de, y no hacer referencia alguna, Foo o Bar hasta que se carguen ambas clases. Si el primer módulo se carga primero, no cree un Bar o consulte Bar hasta que se ejecute la instrucción class Foo. De manera similar, si el segundo módulo se carga primero, no cree un Foo o haga referencia a Foo hasta que se ejecute la instrucción class Bar.

Esta es básicamente la fuente de la ImportError, que podría evitarse si se hizo "foo importación" y "barra de importación" en su lugar, y acostumbrado foo.Foo donde ahora se utiliza Foo y bar.Bar donde ahora se utiliza Bar. Al hacerlo, ya no se referirá a ninguno de ellos hasta que se cree un Foo o Bar, lo cual no sucederá hasta que ambos sean creados (de lo contrario obtendrá un AttributeError).

2

Se podía aplazar la importación en bar.py así:

class Bar(object): 

    def __init__(self, arg_instance_of_foo): 
     from foo import Foo 
     if not isinstance(arg_instance_of_foo, Foo): 
      raise TypeError() 
+1

Todas las importaciones deben ir en la parte superior, a menos que haya una * realmente * buena razón para no hacerlo. Esto no es todo. –

+0

@Devin: Su respuesta es mejor, pero esta es una forma estándar de manejar importaciones circulares por lo que la respuesta aún vale la pena. – FogleBird

+0

@Devin Jeanpierre: ¿Qué más podría hacer? –

4

Puede programar contra interface (ABC - abstract base class en Python), y no tipo específico Bar. Esta es una forma clásica de resolver interdependencias de paquete/módulo en muchos idiomas.Conceptualmente, también debería dar como resultado un mejor diseño del modelo de objetos.

En su caso, debe definir la interfaz IBar en algún otro módulo (o incluso en el módulo que contiene la clase Foo - depende del uso de esa abc). Usted código a continuación, se ve así:

foo.py:

from bar import Bar, IFoo 

class Foo(IFoo): 
    def __init__(self): 
     self.__bar = Bar(self) 

# todo: remove this, just sample code 
f = Foo() 
b = Bar(f) 
print f 
print b 
x = Bar('do not fail me please') # this fails 

bar.py:

from abc import ABCMeta 
class IFoo: 
    __metaclass__ = ABCMeta 

class Bar(object): 
    def __init__(self, arg_instance_of_foo): 
     if not isinstance(arg_instance_of_foo, IFoo): 
      raise TypeError() 
+1

>> :) bastante justo. En Java y otros lenguajes fuertes, estos problemas aparecen con bastante frecuencia y tienen algunas soluciones "estándar". Esto "no es un problema" en Python, donde el tipado de patos es solo "el camino". No obstante, asumí que una vez que se ha formulado la pregunta, existe un "requisito" para garantizar el tipo. – van

Cuestiones relacionadas