Python 3.5 “ImportError: no se puede importar el nombre ‘SomeName’

Estoy tratando de implementar una pequeña biblioteca para Python 3.5, pero sigo luchando con cómo manejar correctamente la estructuración de los paquetes / módulos y cómo hacer que las importaciones funcionen.

Sigo corriendo en el problema donde Python se queja de no poder importar algún nombre con un error como

ImportError: cannot import name 'SubClass1' 

Esto parece suceder cuando “SubClase1” necesita importar algún otro módulo, pero ese otro módulo también necesita saber acerca de SubClase1 (una importación cíclica). Necesito la importación cíclica en mi biblioteca porque la clase base tiene un método de fábrica que crea las instancias de subclase adecuadas (también hay otras situaciones donde se necesitan importaciones cíclicas, por ejemplo, verificar el tipo de un argumento de función necesita la importación de dónde se encuentra ese tipo definido, pero ese módulo puede necesitar la clase donde se realiza esa comprobación: ¡otra dependencia cíclica!)

Aquí está el código de ejemplo:

El directorio raíz contiene el subdirectorio dir1. El directorio dir1 contiene un archivo vacío init.py, un archivo baseclass.py y un archivo subclass1.py. El archivo ./dir1/subclass1.py contiene:

 from . baseclass import BaseClass class SubClass1(BaseClass): pass 

El archivo ./dir1/baseclass.py contiene:

 from . subclass1 import SubClass1 class BaseClass(object): def make(self,somearg): # .. some logic to decide which subclass to create ret = SubClass1() # .. which gets eventually returned by this factory method return ret 

El archivo ./test1.py contiene:

 from dir1.subclass1 import SubClass1 sc1 = SubClass1() 

Esto resulta en el siguiente error:

 Traceback (most recent call last): File "test1.py", line 1, in  from dir1.subclass1 import SubClass1 File "/data/johann/tmp/python1/dir1/subclass1.py", line 1, in  from . baseclass import BaseClass File "/data/johann/tmp/python1/dir1/baseclass.py", line 1, in  from . subclass1 import SubClass1 ImportError: cannot import name 'SubClass1' 

¿Cuál es la forma estándar / mejor de resolver este problema, idealmente de una manera compatible con versiones anteriores de python 2.xy python 3 hasta la versión 3.2?

He leído en otra parte que importar el módulo en lugar de algo de un módulo puede ayudar aquí, pero no sé cómo importar el módulo (por ejemplo, subclase1) de una manera relativa porque “importar. Subclase1” o similar no funciona.

Su problema es causado por una importación circular. El módulo baseclass está intentando importar SubClass1 desde el módulo subclass1 , pero la subclass está intentando importar BaseClass inmediatamente. Obtiene NameError porque las clases aún no se han definido cuando se ejecutan las instrucciones de import .

Hay algunas maneras de resolver el problema.

Una opción sería cambiar su estilo de importación. En lugar de importar las clases por nombre, solo importe los módulos y busque los nombres como atributos más adelante.

 from . import baseclass class SubClass1(baseclass.BaseClass): pass 

Y:

 from . import subclass1 class BaseClass: def make(self,somearg): # ... ret = subclass1.SubClass1() 

Debido a que SubClass1 necesita poder usar BaseClass inmediatamente en el momento de la definición, este código aún puede fallar si el módulo baseclass se importa antes de subclass1 . Así que no es ideal

Otra opción sería cambiar baseclass para hacer su importación debajo de la definición de BaseClass . De esta manera, el módulo de subclass podrá importar el nombre cuando necesite:

 class BaseClass: def make(self,somearg): # .. some logic to decide which subclass to create ret = SubClass1() from .subclass1 import SubClass1 

Esto no es ideal porque el lugar normal para colocar las importaciones está en la parte superior del archivo. Ponerlos en otro lugar hace que el código sea más confuso. Es posible que desee colocar un comentario en la parte superior del archivo que explique por qué está retrasando la importación si sigue esta ruta.

Otra opción puede ser combinar sus dos módulos en un solo archivo. Python no requiere que cada clase tenga su propio módulo como lo hacen otros idiomas. Cuando tiene clases estrechamente acopladas (como las de su ejemplo), tiene mucho sentido colocarlas todas en un solo lugar. Esto le permite evitar todo el problema, ya que no necesita ninguna importación.

Finalmente, hay algunas soluciones más complicadas, como la dependency injection. En lugar de la clase base que necesita saber acerca de las subclases, cada subclase podría registrarse llamando a alguna función y pasando una referencia a sí misma. Por ejemplo:

 # no imports of subclasses! def BaseClass: subclasses = [] def make(self, somearg): for sub in self.subclasses: if sub.accepts(somearg): return sub() raise ValueError("no subclass accepts value {!r}".format(somearg)) @classmethod def register(cls, sub): cls.subclasses.append(sub) return sub # return the class so it can be used as a decorator! 

Y en subclass.py

 from .baseclass import BaseClass @BaseClass.register class SubClass1(BaseClass): @classmethod def accepts(cls, somearg): # put logic for picking this subclass here! return True 

Este estilo de progtwigción es un poco más complicado, pero puede ser bueno, ya que es más fácil de extender que una versión en la que BaseClass necesita conocer todas las subclases que se encuentran al BaseClass . Hay una variedad de formas en que puede implementar este estilo de código, usando una función de register es solo una de ellas. Una cosa buena de esto es que no requiere estrictamente la herencia (por lo que podría registrar una clase que en realidad no hereda de BaseClass si quisiera). Si solo está tratando con subclases heredadas reales, puede considerar usar una metaclase que haga el registro de las subclases de forma automática.