¿Puedo usar __init__.py para definir variables globales?

130

Quiero definir una constante que debería estar disponible en todos los submódulos de un paquete. Pensé que el mejor lugar estaría en el __init__.pyarchivo del paquete raíz. Pero no sé cómo hacer esto. Supongamos que tengo algunos subpaquetes y cada uno con varios módulos. ¿Cómo puedo acceder a esa variable desde estos módulos?

Por supuesto, si esto está totalmente mal y hay una alternativa mejor, me gustaría saberlo.

Andrei Vajna II
fuente

Respuestas:

196

Deberías poder ponerlos __init__.py. Esto se hace todo el tiempo.

mypackage/__init__.py:

MY_CONSTANT = 42

mypackage/mymodule.py:

from mypackage import MY_CONSTANT
print "my constant is", MY_CONSTANT

Luego, importe mymodule:

>>> from mypackage import mymodule
my constant is 42

Aún así, si tiene constantes, sería razonable (mejores prácticas, probablemente) colocarlas en un módulo separado (constantes.py, config.py, ...) y luego, si las desea en el espacio de nombres del paquete, importe ellos.

mypackage/__init__.py:

from mypackage.constants import *

Aún así, esto no incluye automáticamente las constantes en los espacios de nombres de los módulos del paquete. Cada uno de los módulos en el paquete todavía tendrá que importar constantes explícitamente desde mypackageo desde mypackage.constants.

Jason R. Coombs
fuente
19
Esta debería haber sido la respuesta aceptada. Si está trabajando con Python 2.5 o superior, también puede usar una importación relativa explícita, como se describe aquí : from . import MY_CONSTANT
ThatAintWorking
¿no funciona la segunda forma solo para constantes? from mypackage.constants import *colocará copias de MY_CONSTANTcada submódulo en lugar de una referencia a la misma variable
hardmooth
@hardmooth: No exactamente. Los valores se copian por referencia, por lo que si tuviera que mutar MY_CONSTANTen cualquiera de los módulos, mutaría en todas partes. Si MY_CONSTANTtuviera que reasignar alguno de los módulos, solo afectaría a ese módulo. Si esa es su intención, debe hacer referencia por atributo, es decir mypackage.constants.MY_CONSTANT.
Jason R. Coombs el
77
Un retén con el ejemplo es si importa mymodule.pyen __init__.pyantes MY_CONSTANT = 42de que se producirá un error porque cuando la importación mymodule.py MY_CONSTANTno se ha definido todavía. Por lo tanto, debe moverse MY_CONSTANT = 42arribaimport mymodule
markmnl
¿Qué hay de variable? Se trata de variables, no de constantes. ¿Cómo trata Python la variable dentro del paquete? ¿Qué pasa si se ha cambiado la variable dentro del paquete?
TomSawyer
31

Usted no puede hacer eso. Tendrá que importar explícitamente sus constantes en el espacio de nombres de cada módulo individual. La mejor manera de lograr esto es definir sus constantes en un módulo "config" e importarlo donde lo necesite:

# mypackage/config.py
MY_CONST = 17

# mypackage/main.py
from mypackage.config import *
Fernando Beyer
fuente
Sí, un archivo de configuración es lo que me gustaría. Solo pensé que init .py sería un buen lugar. Su solución suena como una práctica estándar. ¿Lo es?
Andrei Vajna II
1
Buen punto. No me di cuenta de que la pregunta era tener las constantes colocadas automáticamente en el espacio de nombres de todos los módulos del paquete.
Jason R. Coombs el
Pero cada vez que un script importa config.py, se ejecuta el código interno. ¿Qué recomienda si el código dentro de config.py se ejecuta solo una vez? Digamos que estoy leyendo un archivo settings.json dentro de config.py y no quiero abrirlo () cada vez que importo config.py.
Augiwan
@UGS Así no es como funciona Python. Cada módulo se ejecuta solo una vez. Cuando se importa por segunda vez, el módulo ya está almacenado en caché sys.modules.
Ferdinand Beyer
@FerdinandBeyer ¡Vaya! Olvidé mencionar que estoy importando config.py desde varios scripts y no desde el mismo script. Digamos que a.py está importando config.py & b.py y b.py está importando config.py. Me preguntaba si es posible asegurarse de que el código dentro de config.py se ejecute solo una vez.
Augiwan
2

Puede definir variables globales desde cualquier lugar, pero es una muy mala idea. importe el __builtin__módulo y modifique o agregue atributos a estos módulos, y de repente tiene nuevas constantes o funciones integradas. De hecho, cuando mi aplicación instala gettext, obtengo la función _ () en todos mis módulos, sin importar nada. Entonces esto es posible, pero por supuesto solo para proyectos de tipo Aplicación, no para paquetes o módulos reutilizables.

Y supongo que nadie recomendaría esta práctica de todos modos. ¿Qué tiene de malo un espacio de nombres? Dicha aplicación tiene el módulo de versión, de modo que tengo variables "globales" disponibles como version.VERSION, version.PACKAGE_NAMEetc.

u0b34a0f6ae
fuente
0

Solo quería agregar que las constantes pueden emplearse usando un archivo config.ini y analizarse en el script usando la biblioteca configparser. De esta manera, podría tener constantes para múltiples circunstancias. Por ejemplo, si tenía constantes de parámetro para dos solicitudes de URL separadas, simplemente etiquételas así:

mymodule/config.ini
[request0]
conn = 'admin@localhost'
pass = 'admin'
...

[request1]
conn = 'barney@localhost'
pass = 'dinosaur'
...

La documentación en el sitio web de Python me pareció muy útil. No estoy seguro de si hay alguna diferencia entre Python 2 y 3, así que aquí están los enlaces a ambos:

Para Python 3: https://docs.python.org/3/library/configparser.html#module-configparser

Para Python 2: https://docs.python.org/2/library/configparser.html#module-configparser

Dan Temkin
fuente