python python-2.7

python - ¿Es una buena práctica usar `import__main__`?



python-2.7 (2)

Hacerlo requiere violar PEP8 , que especifica

Las importaciones siempre se colocan en la parte superior del archivo, justo después de los comentarios de los módulos y las cadenas de documentos, y antes de los módulos globales y constantes.

Para que gui.py pueda importar exitosamente __main__.DEBUG , deberá establecer el valor de DEBUG antes de import gui gui.py de gui.py .

Estoy trabajando en una aplicación relativamente grande de Python, y hay varios recursos que me gustaría mantener como variables globales accesibles a través de varios módulos diferentes. Estos valores son cosas como el número de versión, la fecha de versión, la configuración global y algunas rutas estáticas a los recursos. También DEBUG un indicador de DEBUG que se establece mediante una opción de línea de comando para que pueda ejecutar mi aplicación en un modo de depuración sin necesitar todo el entorno.

Los valores que estoy importando he tenido la precaución de asegurar son los que no cambian en el transcurso de la ejecución del programa, y ​​los he documentado como variables globales constantes que no deberían tocarse. Mi código se ve esencialmente como

# Main.py import wx from gui import Gui DEBUG = False GLOBAL_CONFIG = None VERSION = ''1.0'' ICON_PATH = ''some/path/to/the/app.ico'' def main(): global DEBUG, GLOBAL_CONFIG # Simplified import sys DEBUG = ''--debug'' in sys.argv GLOBAL_CONFIG = load_global_config() # Other set-up for the application, e.g. setting up logging, configs, etc app = wx.App() gui = Gui() app.MainLoop() if __name__ == ''__main__'': main()

# gui.py import wx from __main__ import DEBUG, GLOBAL_CONFIG, ICON_PATH import controller class Gui(wx.Frame): def __init__(self): wx.Frame.__init__(self, None) icon = wx.Icon(ICON_PATH, wx.BITMAP_TYPE_ICO) self.SetIcon(icon) # Always make a copy so we don''t accidentally modify it conf = GLOBAL_CONFIG.copy() self.controller = controller.Controller(conf) # More setup, building the layout, etc

# controller.py from __main__ import DEBUG import logging log = logging.getLogger(''controller'') class Controller(object): def __init__(self, conf): if DEBUG: log.info("Initializing controller in DEBUG mode") self.conf = conf # Other setup ...

Esto obviamente está muy desglosado de lo que realmente es mi aplicación, y descuida el manejo de errores, la documentación y básicamente todos los detalles de implementación.

Ahora, lo he visto decir que esta es una mala idea, pero sin explicación de por qué. Como la mayoría de los resultados al buscar en Google variantes de "python import __main__" son preguntas sobre qué if __name__ == ''__main__'' es difícil encontrar información sólida sobre este tema. Hasta ahora no he tenido problemas con eso, y en realidad ha sido realmente conveniente.

Entonces, ¿esto se considera una buena práctica de Python, o hay alguna razón por la que debería evitar este diseño?


Creo que hay dos razones principales (ja, ja) por las que uno podría prescribir evitar este patrón.

  • Ofusca el origen de las variables que está importando.
  • Se rompe (o al menos es difícil de mantener) si su programa tiene múltiples puntos de entrada. Imagínese si alguien, muy posiblemente usted, quisiera extraer un subconjunto de su funcionalidad en una biblioteca independiente: tendrían que eliminar o redefinir cada una de esas referencias huérfanas para que la cosa se pueda utilizar fuera de su aplicación.

Si tienes un control total sobre la aplicación y nunca habrá otro punto de entrada u otro uso para tus funciones, y estás seguro de que no te importa la ambigüedad, no creo que haya ninguna razón objetiva por la cual la from __main__ import foo patrón from __main__ import foo es malo No me gusta personalmente, pero una vez más, es básicamente por las dos razones anteriores.

Creo que una solución más robusta / amigable para el desarrollador puede ser algo como esto, creando un módulo especial específicamente para contener estas variables súper globales. A continuación, puede importar el módulo y consultar module.VAR cada vez que necesite la configuración. Básicamente, solo crea un espacio de nombres de módulo especial en el que almacenar la configuración de tiempo de ejecución super-global.

# conf.py (for example) # This module holds all the "super-global" stuff. def init(args): global DEBUG DEBUG = ''--debug'' in args # set up other global vars here.

Entonces lo usarías más así:

# main.py import conf import app if __name__ == ''__main__'': import sys conf.init(sys.argv[1:]) app.run()

# app.py import conf def run(): if conf.DEBUG: print(''debug is on'')

Tenga en cuenta el uso de conf.DEBUG lugar de conf.DEBUG from conf import DEBUG . Esta construcción significa que puede alterar la variable durante la vida útil del programa y hacer que ese cambio se refleje en otra parte (asumiendo un único proceso / cadena de caracteres, obviamente).

Otra ventaja es que este es un patrón bastante común, por lo que otros desarrolladores lo reconocerán fácilmente. Es fácilmente comparable con el archivo settings.py utilizado por varias aplicaciones populares (por ejemplo, django ), aunque evité ese nombre en particular porque settings.py es convencionalmente un grupo de objetos estáticos, no un espacio de nombres para los parámetros de tiempo de ejecución. Otros buenos nombres para el módulo de espacio de nombres de configuración descritos anteriormente podrían ser el runtime o los params , por ejemplo.