¿Usar variables globales entre archivos?

209

Estoy un poco confundido acerca de cómo funcionan las variables globales. Tengo un proyecto grande, con alrededor de 50 archivos, y necesito definir variables globales para todos esos archivos.

Lo que hice fue definirlos en mi main.pyarchivo de proyectos , de la siguiente manera:

# ../myproject/main.py

# Define global myList
global myList
myList = []

# Imports
import subfile

# Do something
subfile.stuff()
print(myList[0])

Estoy tratando de utilizar myListen subfile.py, la siguiente

# ../myproject/subfile.py

# Save "hey" into myList
def stuff():
    globals()["myList"].append("hey")

Otra forma lo intenté, pero tampoco funcionó

# ../myproject/main.py

# Import globfile    
import globfile

# Save myList into globfile
globfile.myList = []

# Import subfile
import subfile

# Do something
subfile.stuff()
print(globfile.myList[0])

Y dentro subfile.pytenía esto:

# ../myproject/subfile.py

# Import globfile
import globfile

# Save "hey" into myList
def stuff():
    globfile.myList.append("hey")

Pero de nuevo, no funcionó. ¿Cómo debo implementar esto? Entiendo que no puede funcionar así, cuando los dos archivos no se conocen realmente (bueno, el subfile no conoce main), pero no puedo pensar en cómo hacerlo, sin usar io writing o pickle, que No quiero hacer

Martineau
fuente
En realidad, tu segundo enfoque funciona bien para mí. main.py imprime correctamente "hey". ¿Puede ser más específico sobre lo que usted me dice "no funcionó"?
rodion
@rodion: ciclos de importación: el código en el subfile intenta importar globfile, que en su cuerpo se importa de nuevo
jsbueno
1
NameError: name 'myList' is not defineddesde main.pylíneaprint(globfile.myList[0])

Respuestas:

321

El problema se define myListdesde main.py, pero subfile.pynecesita usarlo. Aquí hay una manera limpia de resolver este problema: mover todos los globales a un archivo, yo llamo a este archivo settings.py. Este archivo es responsable de definir los globales e inicializarlos:

# settings.py

def init():
    global myList
    myList = []

A continuación, subfilepuede importar globales:

# subfile.py

import settings

def stuff():
    settings.myList.append('hey')

Tenga en cuenta que subfileno se llama init(), esa tarea pertenece a main.py:

# main.py

import settings
import subfile

settings.init()          # Call only once
subfile.stuff()         # Do stuff with global var
print settings.myList[0] # Check the result

De esta manera, logra su objetivo mientras evita inicializar variables globales más de una vez.

Hai Vu
fuente
41
Me gusta el enfoque general, pero no todo init(). Los módulos solo se evalúan la primera vez que se importan, por lo que está perfectamente bien inicializar esas variables en el cuerpo del módulo.
Kirk Strauser
19
+1 Kirk: estoy de acuerdo. Sin embargo, mi enfoque previene el caso en que otros módulos modifican globals.myList antes de que se inicie el programa principal.
Hai Vu
2
Debería llamarlo de otra manera que no sean globales, que es un nombre incorporado. PyLint da la advertencia: "Redefiniendo los 'globales' integrados (redefinido-incorporado)"
twasbrillig
Gracias. ¿Alguna idea de cómo eliminar los errores de "Variable indefinida de importación" que aparecen en Eclipse PyDev mediante el uso de esta estructura de archivos (es decir, la importación de variables globales de settings.py)? Tuve que deshabilitar el error en PyDev , que no es lo ideal.
Franck Dernoncourt
@FranckDernoncourt Lo siento, no uso Eclipse, así que estoy aún más despistado que tú.
Hai Vu
94

Consulte el documento de Python sobre cómo compartir variables globales entre módulos :

La forma canónica de compartir información entre módulos dentro de un solo programa es crear un módulo especial (a menudo llamado config o cfg).

config.py:

x = 0   # Default value of the 'x' configuration setting

Importe el módulo de configuración en todos los módulos de su aplicación; el módulo estará disponible como nombre global.

main.py:

import config
print (config.x)

o

from config import x
print (x)

En general, no use from modulename import * . Hacerlo abarrota el espacio de nombres del importador y hace que sea mucho más difícil para los linters detectar nombres indefinidos.

Ogaga Uzoh
fuente
1
Salvavidas - solo por ese enlace!
toonarmycaptain
44
Esto parece un enfoque más limpio que la respuesta aceptada.
JoeyC
2
Tenga en cuenta que no puede configurar xusando from config import x, solo usandoimport config
Yariv
1
¡La solución más simple! Gracias
user1297406
22

Puede pensar en las variables globales de Python como variables de "módulo" y, como tales, son mucho más útiles que las "variables globales" tradicionales de C.

Una variable global se define realmente en un módulo __dict__y se puede acceder desde fuera de ese módulo como un atributo del módulo.

Entonces, en tu ejemplo:

# ../myproject/main.py

# Define global myList
# global myList  - there is no "global" declaration at module level. Just inside
# function and methods
myList = []

# Imports
import subfile

# Do something
subfile.stuff()
print(myList[0])

Y:

# ../myproject/subfile.py

# Save "hey" into myList
def stuff():
     # You have to make the module main available for the 
     # code here.
     # Placing the import inside the function body will
     # usually avoid import cycles - 
     # unless you happen to call this function from 
     # either main or subfile's body (i.e. not from inside a function or method)
     import main
     main.mylist.append("hey")
jsbueno
fuente
1
wow, normalmente uno esperaría que dos archivos que se importan entre sí entren en un ciclo infinito.
Nikhil VJ
2
ja A primera vista se ve así, ¿no? Lo que sucede en def stuff () es que la importación no se ejecuta cuando se carga el archivo ... solo se ejecuta cuando se llama a la función stuff (). Entonces, comenzando con main, importamos subfile y luego llamamos a subfile.stuff () que luego importa main ... no loop, solo importa una vez en main. Consulte la nota en el ejemplo subfile.py sobre los ciclos de importación.
John
11

Usar from your_file import *debería solucionar tus problemas. Define todo para que esté disponible globalmente (con la excepción de las variables locales en las importaciones, por supuesto).

por ejemplo:

##test.py:

from pytest import *

print hello_world

y:

##pytest.py

hello_world="hello world!"
IT Ninja
fuente
44
Excepto si asigna a una de esas variables
jsbueno
55
Yo personalmente evito el uso import *a toda costa para que las referencias sean explícitas (y no confusas). Además, ¿cuándo alguna vez ha usado todas las " *" referencias en algún módulo?
ThorSummoner
19
NO importar *. Sus variables globales ya no permanecerán sincronizadas. Cada módulo recibe su propia copia. Cambiar la variable en un archivo no se reflejará en otro. También está advertido en docs.python.org/2/faq/…
Isa Hassen
8

La respuesta de Hai Vu funciona muy bien, solo un comentario:

En caso de que esté utilizando el global en otro módulo y desee establecer el global dinámicamente, preste atención para importar los otros módulos después de configurar las variables globales, por ejemplo:

# settings.py
def init(arg):
    global myList
    myList = []
    mylist.append(arg)


# subfile.py
import settings

def print():
    settings.myList[0]


# main.py
import settings
settings.init("1st")     # global init before used in other imported modules
                         # Or else they will be undefined

import subfile    
subfile.print()          # global usage
lastboy
fuente
4

Su segundo intento funcionará perfectamente, y en realidad es una muy buena manera de manejar nombres de variables que desea tener disponibles globalmente. Pero tienes un error de nombre en la última línea. Así es como debería ser:

# ../myproject/main.py

# Import globfile    
import globfile

# Save myList into globfile
globfile.myList = []

# Import subfile
import subfile

# Do something
subfile.stuff()
print(globfile.myList[0])

¿Ves la última línea? myList es un atributo de globfile, no subfile. Esto funcionará como quieras.

Miguel

MikeHunter
fuente