Presta atención porque en este post vas a hallar la solución que buscas.
Solución:
Aquí es donde está pasando.
Primero, las únicas variables globales que Python realmente tiene son variables de ámbito de módulo. No se puede hacer una variable que sea verdaderamente global; todo lo que puede hacer es crear una variable en un ámbito particular. (Si crea una variable dentro del intérprete de Python y luego importa otros módulos, su variable está en el alcance más externo y, por lo tanto, global dentro de su sesión de Python).
Todo lo que tiene que hacer para hacer una variable global de módulo es simplemente asignar un nombre.
Imagina un archivo llamado foo.py, que contiene esta única línea:
X = 1
Ahora imagina que lo importas.
import foo
print(foo.X) # prints 1
Sin embargo, supongamos que desea utilizar una de sus variables de alcance de módulo como global dentro de una función, como en su ejemplo. El valor predeterminado de Python es asumir que las variables de función son locales. Simplemente agrega un global
declaración en su función, antes de intentar usar global.
def initDB(name):
global __DBNAME__ # add this line!
if __DBNAME__ is None: # see notes below; explicit test for None
__DBNAME__ = name
else:
raise RuntimeError("Database name has already been set.")
Por cierto, para este ejemplo, el simple if not __DBNAME__
prueba es adecuada, porque cualquier string valor que no sea un vacío string evaluará true, por lo que cualquier nombre de base de datos real evaluará true. Pero para las variables que pueden contener un valor numérico que podría ser 0, no puede simplemente decir if not variablename
; en ese caso, debe probar explícitamente para None
utilizando el is
operador. Modifiqué el ejemplo para agregar un explícito None
prueba. La prueba explícita para None
nunca está mal, por lo que lo uso por defecto.
Finalmente, como otros han señalado en esta página, dos guiones bajos iniciales le indican a Python que desea que la variable sea “privada” para el módulo. Si alguna vez haces un import * from mymodule
, Python no importará nombres con dos guiones bajos iniciales en su espacio de nombres. Pero si haces un simple import mymodule
y luego decir dir(mymodule)
verá las variables “privadas” en la lista, y si se refiere explícitamente a mymodule.__DBNAME__
A Python no le importará, solo le permitirá consultarlo. Los guiones bajos iniciales dobles son una pista importante para los usuarios de su módulo de que no desea que vuelvan a vincular ese nombre con algún valor propio.
Se considera una buena práctica en Python no hacer import *
, pero para minimizar el acoplamiento y maximizar la claridad mediante el uso de mymodule.something
o haciendo explícitamente una importación como from mymodule import something
.
EDITAR: Si, por alguna razón, necesita hacer algo como esto en una versión muy antigua de Python que no tiene la global
palabra clave, hay una solución fácil. En lugar de establecer una variable global del módulo directamente, use un tipo mutable en el nivel global del módulo y almacene sus valores dentro de él.
En sus funciones, el nombre de la variable global será de solo lectura; no podrá volver a vincular el nombre de la variable global real. (Si asigna a ese nombre de variable dentro de su función, solo afectará al nombre de variable local dentro de la función). Pero puede usar ese nombre de variable local para acceder al objeto global real y almacenar datos dentro de él.
Puedes usar un list
pero tu código será feo:
__DBNAME__ = [None] # use length-1 list as a mutable
# later, in code:
if __DBNAME__[0] is None:
__DBNAME__[0] = name
A dict
es mejor. Pero lo más conveniente es una instancia de clase, y puede usar una clase trivial:
class Box:
pass
__m = Box() # m will contain all module-level values
__m.dbname = None # database name global in module
# later, in code:
if __m.dbname is None:
__m.dbname = name
(Realmente no necesita poner en mayúscula la variable de nombre de la base de datos).
Me gusta el azúcar sintáctico de usar __m.dbname
en vez de __m["DBNAME"]
; parece la solución más conveniente en mi opinión. Pero el dict
La solución también funciona bien.
Con un dict
puedes usar cualquier valor hash como key, pero cuando esté satisfecho con los nombres que son identificadores válidos, puede usar una clase trivial como Box
en lo anterior.
Acceso explícito a variables de nivel de módulo accediendo a ellas explícitamente en el módulo
En breve: La técnica descrita aquí es la misma que en la respuesta de steveha, excepto, que no se crea ningún objeto auxiliar artificial para establecer el alcance de las variables de forma explícita. En cambio, el objeto del módulo en sí mismo recibe un puntero variable y, por lo tanto, proporciona un alcance explícito al acceso desde cualquier lugar. (como asignaciones en el alcance de la función local).
Piense en ello como uno mismo Para el módulo actual en lugar de la instancia actual!
# db.py
import sys
# this is a pointer to the module object instance itself.
this = sys.modules[__name__]
# we can explicitly make assignments on it
this.db_name = None
def initialize_db(name):
if (this.db_name is None):
# also in local function scope. no scope specifier like global is needed
this.db_name = name
# also the name remains free for local use
db_name = "Locally scoped db_name variable. Doesn't do anything here."
else:
msg = "Database is already initialized to 0."
raise RuntimeError(msg.format(this.db_name))
Como los módulos se almacenan en caché y, por lo tanto, se importan solo una vez, puede importar db.py
tantas veces en tantos clientes como desee, manipulando el mismo estado universal:
# client_a.py
import db
db.initialize_db('mongo')
# client_b.py
import db
if (db.db_name == 'mongo'):
db.db_name = None # this is the preferred way of usage, as it updates the value for all clients, because they access the same reference from the same module object
# client_c.py
from db import db_name
# be careful when importing like this, as a new reference "db_name" will
# be created in the module namespace of client_c, which points to the value
# that "db.db_name" has at import time of "client_c".
if (db_name == 'mongo'): # checking is fine if "db.db_name" doesn't change
db_name = None # be careful, because this only assigns the reference client_c.db_name to a new value, but leaves db.db_name pointing to its current value.
Como beneficio adicional, lo encuentro bastante pitónico en general, ya que se ajusta muy bien a la política de Pythons de Explícito es mejor que implícito.
La respuesta de Steveha fue útil para mí, pero omite un punto importante (uno al que creo que Wisty estaba llegando). La palabra clave global no es necesaria si solo accede pero no asigna la variable en la función.
Si asigna la variable sin la palabra clave global, Python crea una nueva var local; el valor de la variable del módulo ahora estará oculto dentro de la función. Utilice la palabra clave global para asignar el módulo var dentro de una función.
Pylint 1.3.1 bajo Python 2.7 obliga a NO usar global si no asigna la var.
module_var = '/dev/hello'
def readonly_access():
connect(module_var)
def readwrite_access():
global module_var
module_var = '/dev/hello2'
connect(module_var)
Tienes la opción de proteger nuestra investigación fijando un comentario o puntuándolo te lo agradecemos.