texto - tipos de datos que utiliza python
¿Cómo crear variables de módulo completo en Python? (5)
Aquí es donde está pasando.
En primer lugar, las únicas variables globales que Python realmente tiene son las variables con ámbito de módulo. No puedes hacer una variable que sea verdaderamente global; Todo lo que puedes 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 estará en el alcance más externo y, por lo tanto, será global en su sesión de Python).
Todo lo que tiene que hacer para crear una variable módulo global es simplemente asignarle un nombre.
Imagine 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 ámbito de módulo como global dentro de una función, como en su ejemplo. El valor predeterminado de Python es suponer que las variables de función son locales. Simplemente agrega una declaración global
en su función, antes de intentar usar el 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, la prueba simple if not __DBNAME__
es adecuada, porque cualquier valor de cadena que no sea una cadena vacía evaluará verdadero, por lo que cualquier nombre de base de datos real evaluará verdadero. Pero para las variables que podrían contener un valor numérico que podría ser 0, no se puede decir if not variablename
; en ese caso, debe probar explícitamente None
utilizando el operador is
. Modifiqué el ejemplo para agregar una prueba None
explícita. La prueba explícita de None
nunca es incorrecta, por lo que de manera predeterminada la uso.
Finalmente, como han señalado otros en esta página, dos puntos destacados señalan a Python que desea que la variable sea "privada" para el módulo. Si alguna vez realiza una import * from mymodule
, Python no importará nombres con dos guiones bajos principales en su espacio de nombres. Pero si solo hace una import mymodule
y luego dice dir(mymodule)
verá las variables "privadas" en la lista, y si se refiere explícitamente a mymodule.__DBNAME__
Python no le importará, solo le permitirá consultar eso. Los guiones bajos dobles son una pista importante para los usuarios de su módulo de que no desea que vuelvan a enlazar ese nombre con algún valor propio.
Se considera una práctica recomendada en Python no import *
, sino minimizar el acoplamiento y maximizar la explicitud ya sea al utilizar mymodule.something
o al hacer 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 palabra clave global
, existe una solución fácil. En lugar de establecer directamente una variable global de módulo, 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 enlazar el nombre de la variable global real. (Si asigna un nombre de variable dentro de su función, solo afectará el nombre de la 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 una 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
Un dict
es mejor. Pero lo más conveniente es una instancia de clase, y puedes 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 capitalizar la variable del nombre de la base de datos).
Me gusta el azúcar sintáctico de usar __m.dbname
lugar de __m["DBNAME"]
; parece la solución más conveniente en mi opinión. Pero la solución dict
funciona bien también.
Con un dict
puede usar cualquier valor hashable como clave, pero cuando esté satisfecho con los nombres que son identificadores válidos, puede usar una clase trivial como Box
en el párrafo anterior.
¿Hay alguna manera de configurar una variable global dentro de un módulo? Cuando traté de hacerlo de la forma más obvia como aparece a continuación, el intérprete de Python dijo que la variable __DBNAME__
no existía.
...
__DBNAME__ = None
def initDB(name):
if not __DBNAME__:
__DBNAME__ = name
else:
raise RuntimeError("Database name has already been set.")
...
Y después de importar el módulo en un archivo diferente
...
import mymodule
mymodule.initDB(''mydb.sqlite'')
...
Y el rastreo fue: UnboundLocalError: local variable ''__DBNAME__'' referenced before assignment
¿Algunas ideas? Estoy tratando de configurar un singleton usando un módulo, según la recomendación de este compañero .
La respuesta de Steveha fue útil para mí, pero omite un punto importante (uno en el 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. Use la palabra clave global para asignar el módulo var dentro de una función.
Pylint 1.3.1 en Python 2.7 exige que NO se use global si no se 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)
Para esto, necesita declarar la variable como global. Sin embargo, también se puede acceder a una variable global desde fuera del módulo utilizando module_name.var_name
. Agregue esto como la primera línea de su módulo:
global __DBNAME__
Te estás enamorando de una sutil peculiaridad. No puede volver a asignar variables de nivel de módulo dentro de una función de Python. Creo que esto está ahí para evitar que las personas vuelvan a asignar cosas dentro de una función por accidente.
Puede acceder al espacio de nombres del módulo, simplemente no debe intentar reasignar. Si su función asigna algo, se convierte automáticamente en una variable de función, y Python no buscará en el espacio de nombres del módulo.
Tu puedes hacer:
__DB_NAME__ = None
def func():
if __DB_NAME__:
connect(__DB_NAME__)
else:
connect(Default_value)
pero no puedes volver a asignar __DB_NAME__
dentro de una función.
Una solución:
__DB_NAME__ = [None]
def func():
if __DB_NAME__[0]:
connect(__DB_NAME__[0])
else:
__DB_NAME__[0] = Default_value
Tenga en cuenta que no estoy reasignando __DB_NAME__
, solo estoy modificando su contenido.
Acceso explícito a las variables de nivel de módulo accediendo a ellas explícitamente en el módulo
En resumen: 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 hacer un alcance explícito de las variables. En cambio, al objeto del módulo en sí se le asigna un puntero variable y, por lo tanto, proporciona un alcance explícito al acceder desde cualquier lugar. (como asignaciones en el alcance de la función local) .
¡Piense en él como en 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
veces como desee, manipulando el mismo estado universal:
# client_a.py
import db
db.initialize_db(''mongo'')
# client_b.py
from db import db_name
if (db_name == ''mongo''):
db_name = None
He utilizado esta receta para crear módulos manejadores simples y livianos (a diferencia de las clases de controladores) desde que lo encontré. Como una ventaja adicional, creo que es bastante pitonica en general, ya que encaja muy bien con la política de Pythons de Explicit es mejor que implícita .