Me encontré con un poco de un muro al importar módulos en un script de Python. Haré todo lo posible para describir el error, por qué me encuentro con él y por qué estoy vinculando este enfoque en particular para resolver mi problema (que describiré en un segundo):
Supongamos que tengo un módulo en el que he definido algunas funciones / clases de utilidad, que se refieren a entidades definidas en el espacio de nombres en el que se importará este módulo auxiliar (sea "a" una entidad de este tipo):
módulo 1:
def f():
print a
Y luego tengo el programa principal, donde se define "a", en el que quiero importar esas utilidades:
import module1
a=3
module1.f()
La ejecución del programa provocará el siguiente error:
Traceback (most recent call last):
File "Z:\Python\main.py", line 10, in <module>
module1.f()
File "Z:\Python\module1.py", line 3, in f
print a
NameError: global name 'a' is not defined
Se han hecho preguntas similares en el pasado (hace dos días, uh) y se han sugerido varias soluciones, sin embargo, no creo que se ajusten a mis requisitos. Aquí está mi contexto particular:
Estoy tratando de crear un programa Python que se conecte a un servidor de base de datos MySQL y muestre / modifique datos con una GUI. En aras de la limpieza, he definido el grupo de funciones auxiliares / utilitarias relacionadas con MySQL en un archivo separado. Sin embargo, todos tienen una variable común, que había definido originalmente dentro del módulo de utilidades, y que es el objeto cursor del módulo MySQLdb. Más tarde me di cuenta de que el objeto cursor (que se usa para comunicarse con el servidor de base de datos) debe definirse en el módulo principal, de modo que tanto el módulo principal como cualquier cosa que se importe en él puedan acceder a ese objeto.
El resultado final sería algo como esto:
utilities_module.py:
def utility_1(args):
code which references a variable named "cur"
def utility_n(args):
etcetera
Y mi módulo principal:
program.py:
import MySQLdb, Tkinter
db=MySQLdb.connect(#blahblah) ; cur=db.cursor() #cur is defined!
from utilities_module import *
Y luego, tan pronto como intento llamar a cualquiera de las funciones de utilidades, se activa el error "nombre global no definido" mencionado anteriormente.
Una sugerencia particular fue tener una declaración "from program import cur" en el archivo de utilidades, como esta:
utilities_module.py:
from program import cur
#rest of function definitions
program.py:
import Tkinter, MySQLdb
db=MySQLdb.connect(#blahblah) ; cur=db.cursor() #cur is defined!
from utilities_module import *
Pero eso es una importación cíclica o algo así y, en resumen, también falla. Entonces mi pregunta es:
¿Cómo diablos puedo hacer que el objeto "cur", definido en el módulo principal, sea visible para las funciones auxiliares que se importan en él?
Gracias por su tiempo y mis más sinceras disculpas si la solución se ha publicado en otro lugar. No puedo encontrar la respuesta por mí mismo y no tengo más trucos en mi libro.
fetch_all
e iterar a través de dos listas en su lugar , o simplemente para que pueda tener dos hilos / greenlets / callback-chains / lo que sea usando la base de datos sin conflictos).db
(ycur
, si insiste) en un módulo independiente que tantoprogram
yutilities_module
importarlo de. De esa manera, no obtendrá dependencias circulares (programa de importación de módulos que el programa importa) y la confusión que conlleva.Respuestas:
Los globales en Python son globales para un módulo , no para todos los módulos. (Mucha gente está confundida por esto, porque en, digamos, C, un global es el mismo en todos los archivos de implementación a menos que lo establezca explícitamente
static
).Hay diferentes formas de resolver esto, dependiendo de su caso de uso real.
Antes incluso de seguir este camino, pregúntese si esto realmente necesita ser global. ¿Quizás realmente desea una clase,
f
como método de instancia, en lugar de solo una función gratuita? Entonces podrías hacer algo como esto:Si realmente desea un global, pero está ahí para ser utilizado
module1
, configúrelo en ese módulo.Por otro lado, si
a
lo comparten muchos módulos, colóquelo en otro lugar y haga que todos lo importen:… Y, en module1.py:
No utilice una
from
importación a menos que la variable esté destinada a ser una constante.from shared_stuff import a
crearía una nuevaa
variable inicializada a lo que seashared_stuff.a
referido en el momento de la importación, y esta nuevaa
variable no se vería afectada por asignaciones ashared_stuff.a
.O, en el raro caso de que realmente necesite que sea verdaderamente global en todas partes, como un incorporado, agréguelo al módulo incorporado. Los detalles exactos difieren entre Python 2.xy 3.x. En 3.x, funciona así:
fuente
Como solución alternativa, podría considerar establecer variables de entorno en la capa exterior, como esta.
main.py:
mymodule.py:
Como precaución adicional, maneje el caso cuando MYVAL no esté definido dentro del módulo.
fuente
Una función usa los globales del módulo en el que está definida . En lugar de establecer
a = 3
, por ejemplo, debería establecermodule1.a = 3
. Por lo tanto, si desea quecur
esté disponible como entrada globalutilities_module
, configureutilities_module.cur
.Una mejor solución: no use globales. Pase las variables que necesita a las funciones que lo necesiten, o cree una clase para agrupar todos los datos y páselos al inicializar la instancia.
fuente
Esta publicación es solo una observación del comportamiento de Python que encontré. Quizás los consejos que leíste arriba no te sirvan si hiciste lo mismo que yo hice a continuación.
Es decir, tengo un módulo que contiene variables globales / compartidas (como se sugirió anteriormente):
Luego tuve el módulo principal que importa las cosas compartidas con:
y algunos otros módulos que realmente poblaron estos arreglos. Estos son llamados por el módulo principal. Al salir de estos otros módulos, puedo ver claramente que las matrices están pobladas. Pero al volver a leerlos en el módulo principal, estaban vacíos. Esto fue bastante extraño para mí (bueno, soy nuevo en Python). Sin embargo, cuando cambio la forma en que importo sharedstuff.py en el módulo principal a:
funcionó (las matrices se poblaron).
Sólo digo'
fuente
La solución más fácil a este problema en particular habría sido agregar otra función dentro del módulo que hubiera almacenado el cursor en una variable global del módulo. Entonces todas las demás funciones también podrían usarlo.
módulo 1:
programa principal:
fuente
Dado que los globales son específicos del módulo, puede agregar la siguiente función a todos los módulos importados y luego usarla para:
Entonces, todo lo que necesita para transmitir los globales actuales es:
fuente
Como no lo he visto en las respuestas anteriores, pensé en agregar mi solución alternativa simple, que es solo agregar un
global_dict
argumento a la función que requiere los globales del módulo de llamada, y luego pasar el dict a la función al llamar; p.ej:fuente
La forma de OOP de hacer esto sería hacer de su módulo una clase en lugar de un conjunto de métodos independientes. Luego, puede usar
__init__
o un método de establecimiento para establecer las variables del llamador para usar en los métodos del módulo.fuente