¿Cómo hacer una variable de módulo cruzado?

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 llamémosla ‘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 configurar foo antes de importar otros módulos y luego verían el mismo valor para ellos.

No apoyo esta solución de ninguna manera, forma o forma. Pero si agrega una variable al módulo __builtin__ , será accesible como si fuera un global desde cualquier otro módulo que incluya __builtin__ , que es todos ellos, de forma predeterminada.

a.py contiene

 print foo 

b.py contiene

 import __builtin__ __builtin__.foo = 1 import a 

El resultado es que se imprime “1”.

Edición: 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.

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 de Django (aunque en la configuración de las aplicaciones de Django se usa al importar 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 usan este “pseudoglobal” deberían importar el módulo “globalbaz”, y referirse a él usando “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. (He probado esto en un ejemplo de juguete)

Para aclaración, globalbaz.py se ve así:

 var_name = "my_useful_string" 

Creo que hay muchas circunstancias en las que tiene sentido y simplifica la progtwigción para tener algunos elementos globales que se conocen a través de varios módulos (estrechamente acoplados). En este sentido, me gustaría desarrollar un poco 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 uno de estos módulos, lo llamo “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 la “g”. 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 frecuentemente 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 múltiples 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, digamos, gx cuando x no estaba ya definida en g, y un módulo diferente puede acceder a gx. Sin embargo, aunque el intérprete lo permite, este enfoque no es tan transparente, y evito eso. 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.

Puedes 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 puede hacerlo asignando a __builtins__ :

 __builtins__.foo = 'something' print foo 

Además, los módulos en sí mismos son variables a las que se puede acceder desde cualquier módulo. Así que si define un módulo llamado my_globals.py :

 # my_globals.py foo = 'something' 

Entonces puedes usar eso desde cualquier lugar también:

 import my_globals print my_globals.foo 

Usar 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 manera, si terminas necesitando ejecutar algún código cuando se cambia la variable, puedes hacerlo sin romper la interfaz externa de tu módulo.

No suele ser una buena forma de hacer las cosas, ya que usar globals rara vez lo es, pero creo que esta 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 

segundo.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 nombres __builtin__ . Para hacerlo:

 import __builtin__ __builtin__.foo = 'some-value' 

No use los __builtins__ directamente (observe la “s” adicional) – aparentemente, esto puede ser un diccionario o un módulo. Gracias a ΤΖΩΤΖΙΟΥ por señalar esto, se puede encontrar más aquí .

Ahora foo está disponible para su uso en todas partes.

No recomiendo hacer esto en general, pero el uso de esto depende del progtwigdor.

La asignación se debe hacer como se indicó anteriormente, simplemente configurando foo = 'some-other-value' solo lo establecerá en el espacio de nombres actual.

Utilizo esto para un par de funciones primitivas incorporadas 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: puede hacer esto, por supuesto, con filtro y otra línea para probar la longitud cero, o reducir en una especie 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 (consulte, por ejemplo, http://wiki.c2.com/?GlobalVariablesAreBad ) utilizando 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’ ya que se usa para hacer rebotar variables. Actuará como un espacio para definir temporalmente variables globales y atributos de toda la clase de la clase vacía ‘router’.

fuente.py

 import wall def sourcefn(): msg = 'Hello world!' wall.msg = msg wall.router.msg = msg 

Este módulo importa el muro y define una sola función 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 de 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 progtwig es:

global: hola mundo!
enrutador: hola mundo
global: sin mensaje
enrutador: sin mensaje

Los fragmentos de código anteriores muestran que el módulo / global y los mecanismos de clase / clase variable son esencialmente idénticos.

Si se van a compartir muchas variables, la contaminación del espacio de nombres se puede gestionar mediante el uso de varios módulos de tipo de muro, 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.