¿Cómo hacer una variable de módulo cruzado?
Pregunta
La variable __debug__
es útil en parte porque afecta a todos los módulos. Si quiero crear otra variable que funcione de la misma manera, ¿cómo lo haría?
La variable (seamos originales y la llamemos 'foo') no tiene que ser verdaderamente global, en el sentido de que si cambio foo en un módulo, se actualiza en otros. Estaría bien si pudiera establecer foo antes de importar otros módulos y luego verían el mismo valor para ellos.
Solución
No apoyo esta solución de ninguna manera, forma o forma. Pero si agrega una variable al módulo __builtin__
, será accesible como un módulo global desde cualquier otro módulo que incluya __builtin__
, que son todos ellos, de forma predeterminada .
a.py contiene
print foo
b.py contiene
import __builtin__
__builtin__.foo = 1
import a
El resultado es que " 1 " está impreso.
Editar: El módulo __builtin__
está disponible como el símbolo local __builtins__
: esa es la razón de la discrepancia entre dos de estas respuestas. También tenga en cuenta que __builtin__
ha sido renombrado a builtins
en python3.
Otros consejos
Si necesita una variable global de módulos cruzados, tal vez una simple variable global a nivel de módulo sea suficiente.
a.py:
var = 1
b.py:
import a
print a.var
import c
print a.var
c.py:
import a
a.var = 2
Prueba:
$ python b.py
# -> 1 2
Ejemplo del mundo real: global_settings.py (aunque en Django, las configuraciones se usan importando el objeto django.conf.settings
).
Defina un módulo (llámelo " globalbaz ") y tenga las variables definidas dentro de él. Todos los módulos que utilizan este " pseudoglobal " debe importar el " globalbaz " módulo, y consúltelo utilizando " globalbaz.var_name "
Esto funciona independientemente del lugar del cambio, puede cambiar la variable antes o después de la importación. El módulo importado utilizará el último valor. (Probé esto en un ejemplo de juguete)
Para aclaraciones, globalbaz.py se ve así:
var_name = "my_useful_string"
Creo que hay muchas circunstancias en las que tiene sentido y simplifica la programación para tener algunos globales conocidos a través de varios módulos (estrechamente acoplados). En este sentido, me gustaría elaborar un poco sobre la idea de tener un módulo de elementos globales que sea importado por aquellos módulos que necesitan hacer referencia a ellos.
Cuando solo hay un módulo de este tipo, lo nombro " g " ;. En él, asigno valores predeterminados para cada variable que pretendo tratar como global. En cada módulo que utiliza alguno de ellos, no uso " from g import var " ya que esto solo da como resultado una variable local que se inicializa desde g solo en el momento de la importación. Hago la mayoría de las referencias en la forma g.var, y en la " g. & Quot; sirve como un recordatorio constante de que estoy tratando con una variable que es potencialmente accesible a otros módulos.
Si el valor de una variable global de este tipo se usa con frecuencia en alguna función de un módulo, esa función puede hacer una copia local: var = g.var. Sin embargo, es importante tener en cuenta que las asignaciones a var son locales y que g.var global no se puede actualizar sin hacer referencia a g.var explícitamente en una asignación.
Tenga en cuenta que también puede tener varios módulos globales compartidos por diferentes subconjuntos de sus módulos para mantener las cosas un poco más controladas. La razón por la que uso nombres cortos para mis módulos globales es para evitar sobrecargar el código con las ocurrencias de ellos. Con solo un poco de experiencia, se vuelven lo suficientemente mnemotécnicos con solo 1 o 2 caracteres.
Todavía es posible realizar una asignación a, por ejemplo, g.x cuando x no estaba ya definida en g, y un módulo diferente puede acceder a g.x. Sin embargo, aunque el intérprete lo permita, este enfoque no es tan transparente y lo evito. Todavía existe la posibilidad de crear accidentalmente una nueva variable en g como resultado de un error tipográfico en el nombre de la variable para una asignación. A veces, un examen de dir (g) es útil para descubrir cualquier nombre sorpresa que pueda haber surgido por tal accidente.
Puede pasar los globales de un módulo a otro:
En el módulo A:
import module_b
my_var=2
module_b.do_something_with_my_globals(globals())
print my_var
En el Módulo B:
def do_something_with_my_globals(glob): # glob is simply a dict.
glob["my_var"]=3
Las variables globales suelen ser una mala idea, pero puedes hacerlo asignando a __builtins__
:
__builtins__.foo = 'something'
print foo
Además, los módulos en sí mismos son variables a las que puede acceder desde cualquier módulo. Entonces, si define un módulo llamado my_globals.py
:
# my_globals.py
foo = 'something'
Luego puedes usar eso desde cualquier lugar también:
import my_globals
print my_globals.foo
El uso de módulos en lugar de modificar __builtins__
es generalmente una forma más limpia de hacer globales de este tipo.
Ya puedes hacer esto con variables de nivel de módulo. Los módulos son los mismos sin importar de qué módulo se estén importando. Por lo tanto, puede hacer de la variable una variable de nivel de módulo en cualquier módulo en el que tenga sentido colocarla, y acceder a ella o asignarla desde otros módulos. Sería mejor llamar a una función para establecer el valor de la variable, o convertirla en una propiedad de algún objeto singleton. De esa forma, si terminas necesitando ejecutar algún código cuando la variable cambia, puedes hacerlo sin romper la interfaz externa de tu módulo.
Por lo general, no es una buena forma de hacer las cosas, ya que usar globals rara vez lo es, pero creo que es la forma más limpia de hacerlo.
Quería publicar una respuesta de que hay un caso en el que no se encontrará la variable.
Las importaciones cíclicas pueden romper el comportamiento del módulo.
Por ejemplo:
first.py
import second
var = 1
second.py
import first
print(first.var) # will throw an error because the order of execution happens before var gets declared.
main.py
import first
En este ejemplo, debería ser obvio, pero en una base de código grande, esto puede ser realmente confuso.
Esto suena como modificar el espacio de nombre __builtin__
. Para hacerlo:
import __builtin__
__builtin__.foo = 'some-value'
No use el __builtins__
directamente (observe los " s ") adicionales - aparentemente, esto puede ser un diccionario o un módulo. Gracias a & # 932; & # 918; & # 937; & # 932; & # 918; & # 921; & # 927; & # 933; para señalar esto, se puede encontrar más información aquí .
Ahora foo
está disponible para su uso en todas partes.
No recomiendo hacer esto en general, pero el uso de esto depende del programador.
La asignación se debe hacer como se indicó anteriormente, solo configurando foo = 'algún otro valor'
solo lo establecerá en el espacio de nombres actual.
Lo uso para un par de funciones primitivas integradas que sentí que realmente faltaban. Un ejemplo es una función de búsqueda que tiene la misma semántica de uso que filtro, mapa, reducir.
def builtin_find(f, x, d=None):
for i in x:
if f(i):
return i
return d
import __builtin__
__builtin__.find = builtin_find
Una vez que se ejecuta (por ejemplo, al importar cerca de su punto de entrada), todos sus módulos pueden usar find () como si, obviamente, se integrara.
find(lambda i: i < 0, [1, 3, 0, -5, -10]) # Yields -5, the first negative.
Nota: Puedes hacer esto, por supuesto, con un filtro y otra línea para probar la longitud cero, o reducir en un tipo de línea extraña, pero siempre sentí que era extraño.
Podría lograr variables modificables entre módulos (o mutables ) utilizando un diccionario:
# in myapp.__init__
Timeouts = {} # cross-modules global mutable variables for testing purpose
Timeouts['WAIT_APP_UP_IN_SECONDS'] = 60
# in myapp.mod1
from myapp import Timeouts
def wait_app_up(project_name, port):
# wait for app until Timeouts['WAIT_APP_UP_IN_SECONDS']
# ...
# in myapp.test.test_mod1
from myapp import Timeouts
def test_wait_app_up_fail(self):
timeout_bak = Timeouts['WAIT_APP_UP_IN_SECONDS']
Timeouts['WAIT_APP_UP_IN_SECONDS'] = 3
with self.assertRaises(hlp.TimeoutException) as cm:
wait_app_up(PROJECT_NAME, PROJECT_PORT)
self.assertEqual("Timeout while waiting for App to start", str(cm.exception))
Timeouts['WAIT_JENKINS_UP_TIMEOUT_IN_SECONDS'] = timeout_bak
Al iniciar test_wait_app_up_fail
, la duración real del tiempo de espera es de 3 segundos.
Me pregunté si sería posible evitar algunas de las desventajas de usar variables globales (ver, por ejemplo, http: //wiki.c2.com/?GlobalVariablesAreBad ) usando un espacio de nombres de clase en lugar de un espacio de nombres global / módulo para pasar valores de variables. El siguiente código indica que los dos métodos son esencialmente idénticos. Hay una ligera ventaja en el uso de espacios de nombres de clase como se explica a continuación.
Los siguientes fragmentos de código también muestran que los atributos o variables pueden crearse y eliminarse dinámicamente tanto en los espacios de nombres globales / de módulos como en los espacios de nombres de clases.
wall.py
# Note no definition of global variables
class router:
""" Empty class """
Yo llamo a este módulo 'muro' porque se usa para rebotar variables. Actuará como un espacio para definir temporalmente variables globales y atributos de toda la clase de la clase vacía 'router'.
source.py
import wall
def sourcefn():
msg = 'Hello world!'
wall.msg = msg
wall.router.msg = msg
Este módulo importa un muro y define una función única sourcefn
que define un mensaje y lo emite mediante dos mecanismos diferentes, uno a través de globales y otro a través de la función de enrutador. Tenga en cuenta que las variables wall.msg
y wall.router.message
se definen aquí por primera vez en sus respectivos espacios de nombres.
dest.py
import wall
def destfn():
if hasattr(wall, 'msg'):
print 'global: ' + wall.msg
del wall.msg
else:
print 'global: ' + 'no message'
if hasattr(wall.router, 'msg'):
print 'router: ' + wall.router.msg
del wall.router.msg
else:
print 'router: ' + 'no message'
Este módulo define una función destfn
que utiliza los dos mecanismos diferentes para recibir los mensajes emitidos por la fuente. Permite la posibilidad de que la variable 'msg' no exista. destfn
también elimina las variables una vez que se han mostrado.
main.py
import source, dest
source.sourcefn()
dest.destfn() # variables deleted after this call
dest.destfn()
Este módulo llama a las funciones previamente definidas en secuencia. Después de la primera llamada a dest.destfn
, las variables wall.msg
y wall.router.msg
ya no existen.
La salida del programa es:
global: ¡Hola mundo!
enrutador: ¡Hola mundo!
global: ningún mensaje
enrutador: no hay mensaje
Los fragmentos de código anteriores muestran que el módulo / global y los mecanismos de variable de clase / clase son esencialmente idénticos.
Si se van a compartir muchas variables, la contaminación del espacio de nombres se puede administrar mediante el uso de varios módulos de tipo pared, por ejemplo. wall1, wall2 etc. o definiendo varias clases de tipo enrutador en un solo archivo. Este último es ligeramente más ordenado, por lo que quizás representa una ventaja marginal para el uso del mecanismo de clase-variable.