2010-06-25 3 views
13

Vous disposez de trois fichiers: main.py, second.py et common.pyPython de folie variable globale

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" 

seconde. py

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

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

Pourquoi secondTest n'utilise pas le glo bal variables de son programme appelant? À quoi sert d'appeler quelque chose de «global» si, en fait, ce n'est pas le cas?

Que manque-t-il pour que secondTest (ou toute fonction externe que j'appelle de main) reconnaisse et utilise les bonnes variables?

+4

lecture sur les étendues et les espaces de noms en Python http://docs.python.org/ tutorial/classes.html # python-scopes-and-namespaces – jfs

Répondre

11

global signifie global pour ce module, pas pour le programme entier. Lorsque vous faites

from lala import * 

vous ajoutez toutes les définitions de lala comme habitants à ce module .

Donc, dans votre cas, vous obtenez deux copies de GLOBAL_ONE

+0

Si c'est vrai, comment accéder à la deuxième copie de GLOBAL_ONE? Quel est le truc magique qui me manque? (Si vous utilisez le débogueur python, vous pouvez voir que les ID d'objet changent: lorsque vous imprimez GLOBAL_ONE, après l'avoir assigné dans main(), lorsque vous y accédez dans secondTest, puis revenez au second ID lorsque vous l'imprimez une deuxième fois dans main().) – Ankh

+1

Pas question si vous ne voulez rien changer. Soit utiliser la solution proposée par @PreludeAndFugue ou changer votre architecture à la meilleure comme d'autres le suggèrent. – nkrkv

5

La première et la question évidente est pourquoi?

Il existe quelques situations dans lesquelles les variables globales sont nécessaires/utiles, mais celles-ci sont en effet peu nombreuses.

Votre problème concerne les espaces de noms. Lorsque vous importez Common dans second.py, GLOBAL_ONE provient de cet espace de noms. Lorsque vous importez secondTest, il fait toujours référence à GLOBAL_ONE depuis common.py.

Votre vrai problème, cependant, est avec la conception. Je ne peux pas penser à une seule raison logique bonne d'implémenter une variable globale de cette façon. Les variables globales sont une affaire délicate en Python car il n'existe pas de variable constante. Cependant, la convention est que lorsque vous voulez garder quelque chose de constant en Python, vous le nommez WITH_ALL_CAPS. Ergo:

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

Il y a beaucoup de raisons qui font quelque chose comme ceci:

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

est terribles.

Bien sûr, si vous faites cela simplement pour comprendre les espaces de noms et l'appel global, continuez.

Sinon, certaines des raisons que les variables globales sont une mauvaise chose ™ sont:

  • pollution Namespace
  • Intégration fonctionnelle - vous voulez que vos fonctions à compartimentés
  • effets secondaires fonctionnels - ce se produit lorsque vous écrivez une fonction qui modifie la variable globale balance et que vous ou quelqu'un d'autre réutilise votre fonction et n'en tient pas compte? Si vous calculiez le solde du compte, tout d'un coup, vous en avez trop ou pas assez. Des bugs comme celui-ci sont difficiles à trouver.

Si vous avez une fonction qui a besoin d'une valeur, vous devez lui transmettre cette valeur en tant que paramètre, sauf si vous avez une raison valable. Une raison serait d'avoir un global de PI - en fonction de vos besoins de précision, vous voudrez peut-être 3.14, ou vous pouvez le vouloir 3.14159265 ... mais c'est un cas où un global a du sens. Il y a probablement seulement une poignée ou deux cas réels qui peuvent utiliser correctement les globals. L'un des cas sont des constantes dans la programmation de jeux. Il est plus facile d'importer pygame.locals et d'utiliser KP_UP que de se souvenir de la valeur entière répondant à cet événement. Ce sont exceptions à la règle.

Et (au moins dans pygame) ces constantes sont stockées dans un fichier séparé - juste pour les constantes. Tout module qui a besoin de ces constantes va importer ces constantes. Lorsque vous programmez, vous écrivez des fonctions pour décomposer votre problème en blocs gérables. De préférence une fonction devrait faire une chose, et n'ont pas d'effets secondaires. Cela signifie qu'une fonction telle que calculatetime() doit calculer l'heure. Il ne devrait probablement pas aller lire un fichier qui contient l'heure, et interdire qu'il devrait faire quelque chose comme écrire l'heure quelque part. Il peut retourner le temps, et prendre des paramètres si elle en a besoin - les deux sont des choses bonnes, acceptables pour les fonctions à faire. Les fonctions sont une sorte de contrat entre vous (le programmeur de la fonction) et n'importe qui (y compris vous) qui utilise la fonction. L'accès à et la modification des variables globales constituent une violation de ce contrat car la fonction peut modifier les données externes de manière non définie ou attendue. Lorsque j'utilise cette fonction calculatetime(), je m'attends à ce qu'il calcule l'heure et la renvoie probablement, ne modifie pas le temps variable global qui répond à l'heure du module que je viens d'importer.

La modification des variables globales rompt le contrat et la distinction logique entre les actions que votre programme prend. Ils peuvent introduire des bogues dans votre programme. Ils rendent difficile la mise à niveau et la modification des fonctions. Lorsque vous utilisez en tant que variables au lieu globals de constante, death awaits you with sharp pointy teeth!

+0

Je suis confus par votre suggestion de changer le design; s'il vous plaît énumérer comment garder une variable globale dans un fichier appelé 'globals.py' est différent de mon placement dans un fichier appelé 'common.py'? Selon quelques autres réponses que j'ai vues ici (je n'ai pas de liens pratiques, pardonnez-moi s'il vous plaît), ma solution est précisément ce qui est recommandé. – Ankh

1

Permettez-moi d'abord de dire que je suis d'accord avec tout le monde qui a répondu avant de dire que c'est probablement pas ce que vous voulez faire. Mais au cas où vous êtes vraiment sûr que c'est la voie à suivre, vous pouvez faire ce qui suit. Au lieu de définir GLOBAL_ONE comme une chaîne dans common.py, définissez-la comme une liste, c'est-à GLOBAL_ONE = ["Frank"]. Ensuite, vous lisez et modifiez GLOBAL_ONE[0] au lieu de GLOBAL_ONE et tout fonctionne comme vous le souhaitez. Notez que je ne pense pas que ce soit un bon style et il y a probablement de meilleurs moyens de réaliser ce que vous voulez vraiment.

+0

Cela semble fonctionner - je ne l'ai pas encore essayé - alors merci. Pourquoi, cependant, cela devrait-il fonctionner différemment de toute autre variable? Est-ce dû à une différence dans la gestion de différents types d'objets? – Ankh

+0

Les chaînes sont immuables en python. – iondiode

+0

@Ankh: Non, car au lieu d'assigner une nouvelle valeur à la variable globale, vous modifiez simplement quelque chose à propos de la valeur qu'il contient déjà: la liste, et en particulier la valeur qu'elle contient. – javawizard

2

Comparez les résultats de ce qui suit au vôtre. Lorsque vous utilisez les espaces de noms corrects, vous obtiendrez les résultats attendus.

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

Cela fonctionne et ne fonctionne pas. Eh bien, laissez-moi clarifier - cela permettra à secondTest de "voir" GLOBAL_ONE. Cependant, dans le fichier de code actuel (qui est beaucoup plus complexe que mes exemples), à la fois main.py et common.py contiennent beaucoup plus d'informations que ces simples exemples de fichiers; Je ne crois certainement pas que concaténation "commun". sur le devant de chaque variable ou fonction que j'appelle, chaque fois que je les utilise, est une bonne réponse. En fait, cela ressemble à un slogan mal conçu de la part de Python. (Si c'est ma seule option, eh bien, nous y allons, je suppose que je dois l'utiliser.) – Ankh

+0

Je ne suggère pas que vous devriez faire les choses de cette façon. Ceci est un exemple à comparer avec le vôtre. Le commentaire de J.F.Sebastian à votre question est ce que vous devriez faire. Je ne pense pas qu'il y ait un problème avec Python. Une fois que vous aurez compris les étendues et les espaces de noms, vous serez mieux à même de repenser votre code pour travailler plus efficacement avec Python. –