2011-04-21 13 views
21

Acabo de heredar un código que me inquieta: hay una biblioteca de pruebas, llena de clases correspondientes a páginas web en nuestro sitio, y cada clase de página web tiene métodos para automatizar la funcionalidad en esa página.¿Manera pitónica de resolver declaraciones circulares de importación?

Existen métodos para hacer clic en el enlace entre páginas, que devuelve la clase de la página vinculada. He aquí un ejemplo simplificado:

homePageLib.py del archivo:

class HomePage(object): 
    def clickCalendarLink(self): 
     # Click page2 link which navigates browswer to page2 
     print "Click Calendar link" 
     # Then returns the page2 object 
     from calendarLib import CalendarPage 
     return CalendarPage() 

calendarLib.py del archivo:

class CalendarPage(object): 
    def clickHomePageLink(self): 
     # Click page1 link which navigates browswer to page1 
     print "Click Home Page link" 
     # Then return the page2 object 
     from homePageLib import HomePage 
     return HomePage() 

Esto entonces permite que el archivo de secuencia de comandos para hacer clic en las páginas y obtener el objeto como un retorno valor de ese método, lo que significa que el autor del guion no tendrá que crear instancias de nuevas páginas mientras navegan por el sitio. (Siento que este es un diseño extraño, pero no puedo decir exactamente por qué, aparte de eso, parece extraño tener un método llamado 'clickSomeLink' y devolver un objeto de la página resultante.)

la siguiente secuencia de comandos muestra cómo un script podría navegar por el sitio: (inserté print page para mostrar cómo cambia la página de objetos)

archivo de guión:

from homePageLib import HomePage 

page = HomePage()  
print page 
page = page.clickCalendarLink() 
print page 
page = page.clickHomePageLink() 
print page 

que produce el siguiente resultado:

<homePageLib.HomePage object at 0x00B57570> 
Click Calendar link 
<calendarLib.CalendarPage object at 0x00B576F0> 
Click Home Page link 
<homePageLib.HomePage object at 0x00B57570> 

Por lo tanto, la parte de este que específicamente me siento más incómoda son las líneas from ____ import ____ que terminan en todas partes. Esto me parece malo por las siguientes razones:

  1. Siempre he hecho una convención para poner todas las declaraciones de importación en la parte superior de un archivo.
  2. Dado que puede haber múltiples enlaces a una página, esto da como resultado la misma línea de código from foo import bar en varios lugares de un archivo.

El problema es que si ponemos estas declaraciones de importación en la parte superior de la página, obtenemos errores de importación, porque (según este ejemplo), las importaciones HomePage CalendarPage y viceversa: homePageLib

Archivo. py

from calendarLib import CalendarPage 

class HomePage(object): 
    def clickCalendarLink(self): 
     # Click page2 link which navigates browswer to page2 
     print "Click Calendar link" 
     # Then returns the page2 object 

     return CalendarPage() 

archivo calendarLib.py

from homePageLib import HomePage 

class CalendarPage(object): 
    def clickHomePageLink(self): 
     # Click page1 link which navigates browswer to page1 
     print "Click Home Page link" 
     # Then return the page2 object 
     return HomePage() 

Esto da como resultado el siguiente error:

>>> from homePageLib import HomePage 
Traceback (most recent call last): 
    File "c:\temp\script.py", line 1, in ? 
    #Script 
    File "c:\temp\homePageLib.py", line 2, in ? 
    from calendarLib import CalendarPage 
    File "c:\temp\calendarLib.py", line 2, in ? 
    from homePageLib import HomePage 
ImportError: cannot import name HomePage 

(consejos sobre cómo mejorar la salida de formato Python?)

En lugar de perpetuar este estilo, me gustaría encontrar una mejor manera. ¿Hay alguna manera Pythonic de lidiar con dependencias circulares como esta y aún mantener las instrucciones de importación en la parte superior del archivo?

Respuesta

46

La resolución de estos constructos generalmente implica técnicas como Dependency Injection.

Es, sin embargo, bastante simple para corregir este error:

En calendarLib.py:

import homePageLib 

class CalendarPage(object): 
    def clickHomePageLink(self): 
     [...] 
     return homePageLib.HomePage() 

El código a nivel de módulo se ejecuta en el momento de la importación. El uso de la sintaxis from [...] import [...] requiere que el módulo se inicialice por completo para que tenga éxito.

Un simple import [...] no lo hace, porque no se accede a ningún símbolo, rompiendo así la cadena de dependencia.

+4

Ya veo. ¿Por qué es eso que funciona? homePageLib todavía importa calendarPageLib y viceversa. – Nathan

+0

@nathan: mire esto para obtener más información. http://stackoverflow.com/questions/710551/import-module-or-from-module-import – chuan

-12

Sí. Refactor para dividir las cosas en piezas más pequeñas e independientes.

+0

La pregunta era sobre cómo refactorizarlo adecuadamente para hacer las piezas independientes. –

0

Por favor, lea Sebastian's answer para una explicación más detallada. Este enfoque fue propuesto por David Beazley en PyCon

intente colocar las importaciones en la parte superior como esto

try: 
    from homePageLib import HomePage 
except ImportError: 
    import sys 
    HomePage = sys.modules[__package__ + '.HomePage'] 

Este intentará importar su HomePage y si no, va a tratar de cargarlo desde la caché

Cuestiones relacionadas