2010-06-25 15 views
13

Tiene tres archivos: main.py, second.py y common.pyPython locura variable global

common.py

#!/usr/bin/python 
GLOBAL_ONE = "Frank" 

main.py

#!/usr/bin/python 
from common import * 
from second import secondTest 

if __name__ == "__main__": 
    global GLOBAL_ONE 
    print GLOBAL_ONE #Prints "Frank" 
    GLOBAL_ONE = "Bob" 
    print GLOBAL_ONE #Prints "Bob" 

    secondTest() 

    print GLOBAL_ONE #Prints "Bob" 

segunda. py

#!/usr/bin/python 
from common import * 

def secondTest(): 
    global GLOBAL_ONE 
    print GLOBAL_ONE #Prints "Frank" 

¿Por qué secondTest no utiliza el glo? variables bal de su programa de llamada? ¿De qué sirve llamar a algo "global" si, de hecho, no lo es?

¿Qué es lo que me falta para obtener SecondTest (o cualquier función externa que llamo desde main) para reconocer y usar las variables correctas?

+4

lectura sobre los ámbitos y espacios de nombres en Python http://docs.python.org/ tutorial/classes.html # python-scopes-and-namespaces – jfs

Respuesta

11

global significa global para este módulo, no para todo el programa. Cuando lo haga

from lala import * 

agrega todas las definiciones de lalacomo locales a este módulo.

Así que en su caso se obtienen dos copias de GLOBAL_ONE

+0

Si eso es cierto, ¿cómo puedo acceder a la segunda copia de GLOBAL_ONE? ¿Cuál es el truco mágico que me estoy perdiendo? (Si usa el depurador de python, puede ver que los ID de objeto cambian: la primera vez que imprime GLOBAL_ONE, después de asignarlo en main(), cuando accede a él en secondTest, y luego vuelve al segundo ID cuando lo imprime una segunda vez en main().) – Ankh

+1

De ninguna manera si no quieres cambiar nada. Use la solución propuesta por @PreludeAndFugue o cambie su arquitectura a la mejor, como sugieren otros. – nkrkv

5

La primera y obvia pregunta es por qué?

Hay algunas situaciones en las que las variables globales son necesarias/útiles, pero son pocas.

Su problema es con espacios de nombres. Cuando importa común en second.py, GLOBAL_ONE proviene de ese espacio de nombres. Cuando importa secondTest, todavía hace referencia a GLOBAL_ONE desde common.py.

Su problema real, sin embargo, es con el diseño. No puedo pensar en un solo motivo lógico bueno razón para implementar una variable global de esta manera. Las variables globales son un asunto complicado en Python porque no existe una variable constante. Sin embargo, la convención es que cuando desea mantener algo constante en Python, llámelo WITH_ALL_CAPS. Ergo:

somevar = MY_GLOBAL_VAR # good! 
MY_GLOBAL_VAR = somevar # What? You "can't" assign to a constant! Bad! 

Hay un montón de razones por las que hacer algo como esto:

earth = 6e24 
def badfunction(): 
    global earth 
    earth += 1e5 
print '%.2e' % earth 

es terrible.

Por supuesto, si solo está haciendo esto como ejercicio para entender los espacios de nombres y la llamada global, continúe.

Si no es así, algunas de las razones por las que las variables globales son una mala cosa ™ son:

  • contaminación Espacio de nombres
  • integración funcional - desea que sus funciones sean compartimentados
  • efectos secundarios funcionales - lo sucede cuando escribe una función que modifica la variable global balance y usted u otra persona está reutilizando su función y no la toma en cuenta? Si estaba calculando el saldo de la cuenta, de repente tiene demasiado o no tiene suficiente. Los errores como este son difíciles de encontrar.

Si tiene una función que necesita un valor, debe pasarlo como un parámetro, a menos que tenga una razón muy buena de lo contrario. Una de las razones sería tener un mundial de PI - dependiendo de su precisión necesita puede que quiera que sea 3.14, o es posible que desee que 3.14159265 ... pero eso es una caso en que un mundial tiene sentido. Probablemente haya solo un puñado o dos de casos del mundo real que pueden usar elementos globales correctamente. Uno de los casos son constantes en la programación de juegos. Es más fácil importar pygame.locals y utilizar KP_UP que recordar el valor entero que responde a ese evento. Estas son excepciones a la regla.

Y (al menos en pygame) estas constantes se almacenan en un archivo separado - sólo para las constantes. Cualquier módulo que necesite esas constantes importará dichas constantes.

Al programar, escribir funciones para romper su problema en trozos manejables. Preferiblemente, una función debe hacer una cosa, y no tener efectos secundarios. Eso significa que una función como calculatetime() debe calcular el tiempo. Probablemente no debería ir a leer un archivo que contiene el tiempo, y prohibir que haga algo como escribir el tiempo en algún lugar. Puede devolver el tiempo, y tomar los parámetros si los necesita, ambos son cosas buenas y aceptables para las funciones. Las funciones son una especie de contrato entre usted (el programador de la función) y cualquier persona (incluido usted) que use la función. Acceder a y cambiar las variables globales son una violación de ese contrato porque la función puede modificar los datos externos de formas que no están definidas o previstas. Cuando uso esa función calculatetime(), espero que calcule el tiempo y probablemente lo devuelva, no modifique el tiempo de la variable global que responde al tiempo del módulo que acabo de importar.

Modificación de las variables globales romper el contrato y la distinción lógica entre las acciones que el programa toma. Pueden introducir errores en su programa. Hacen que sea difícil actualizar y modificar funciones. Cuando se utiliza variables globales como variables en vez de constantes, death awaits you with sharp pointy teeth!

+0

Estoy confundido por su sugerencia de cambiar el diseño; por favor, enumere cómo mantener una variable global en un archivo llamado 'globals.py' es diferente de la ubicación en un archivo llamado 'common.py'? De acuerdo con algunas otras respuestas que he visto aquí (no tengo enlaces útiles, por favor, perdónenme), mi solución es precisamente lo que se recomienda. – Ankh

1

Permitidme que decir que estoy de acuerdo con todos los demás que respondió antes de decir que este es probablemente no lo que quiere hacer. Pero en caso de que esté realmente seguro de que este es el camino a seguir, puede hacer lo siguiente. En lugar de definir GLOBAL_ONE como una cadena en common.py, defínalo como una lista, es decir, GLOBAL_ONE = ["Frank"]. Luego, lea y modifique GLOBAL_ONE[0] en lugar de GLOBAL_ONE y todo funcione de la manera que desee. Tenga en cuenta que no creo que este sea un buen estilo y que probablemente haya mejores formas de lograr lo que realmente desea.

+0

Parece que podría funcionar, todavía no lo he probado, así que gracias. ¿Por qué, sin embargo, debería funcionar esto de manera diferente que cualquier otra variable? ¿Se debe a una diferencia en el manejo de diferentes tipos de objetos? – Ankh

+0

Las cadenas son inmutables en python. – iondiode

+0

@Ankh: No, es porque en lugar de asignar un nuevo valor a la variable global, solo está modificando algo sobre el valor que ya tiene: la lista, y particularmente el valor que contiene. – javawizard

2

Compare los resultados de los siguientes con los suyos. Cuando utiliza los espacios de nombres correctos, obtendrá los resultados que espera.

common.py

#!/usr/bin/python 
GLOBAL_ONE = "Frank" 

main.py

#!/usr/bin/python 
from second import secondTest 
import common 

if __name__ == "__main__": 
    print common.GLOBAL_ONE # Prints "Frank" 
    common.GLOBAL_ONE = "Bob" 
    print common.GLOBAL_ONE # Prints "Bob" 

    secondTest() 

    print common.GLOBAL_ONE # Prints "Bob" 

second.py

#!/usr/bin/python 
import common 

def secondTest(): 
    print common.GLOBAL_ONE # Prints "Bob" 
+0

Esto funciona y no funciona. Bueno, déjenme aclarar: esto permitirá que secondTest "vea" GLOBAL_ONE. Sin embargo, en el archivo de código real (que es mucho más complejo que mis ejemplos), tanto main.py como common.py contienen mucha más información en ellos que estos simples archivos de ejemplo; Ciertamente no creo esa concatenación "común". al frente de cada variable o función que llamo, cada vez que las uso, es una gran respuesta. De hecho, parece una crítica mal pensada por parte de Python. (Si esa es mi única opción, bueno, ahí vamos, supongo que tengo que usarla). – Ankh

+0

No sugiero que debas hacer las cosas de esta manera. Este es un ejemplo para comparar con el tuyo. El comentario de J.F.Sebastian a su pregunta es qué debe hacer. No creo que haya un problema con Python. Una vez que comprenda los ámbitos y espacios de nombres, estará en una mejor posición para rediseñar su código y trabajar de manera más efectiva en Python. –