Variables globales de la fonction Python?

272

Je sais que je devrais éviter d'utiliser des variables globales en premier lieu à cause d'une confusion comme celle-ci, mais si je devais les utiliser, est-ce que la manière suivante est valable pour les utiliser? (J'essaie d'appeler la copie globale d'une variable créée dans une fonction distincte.)

x = "somevalue"

def func_A ():
   global x
   # Do things to x
   return x

def func_B():
   x = func_A()
   # Do things
   return x

func_A()
func_B()

Est-ce xque le que la deuxième fonction utilise a la même valeur que la copie globale xqui func_autilise et modifie? Lors de l'appel des fonctions après la définition, l'ordre est-il important?

Akshat Shekhar
la source
1
veillez également à ne pas supposer simplement parce que vous avez une variable affectée dans votre fonction que python traitera les références avant l'affectation comme telles. Jusqu'à la première affectation, si vous utilisiez x, ce ne serait pas le global, ni le local. Vous obtiendrez la fameuse exception UnboundLocalError dans votre visage :)
osirisgothra

Réponses:

413

Si vous voulez simplement accéder à une variable globale, vous utilisez simplement son nom. Cependant, pour changer sa valeur, vous devez utiliser le globalmot - clé.

Par exemple

global someVar
someVar = 55

Cela changerait la valeur de la variable globale à 55. Sinon, cela assignerait simplement 55 à une variable locale.

L'ordre des listes de définitions de fonctions n'a pas d'importance (en supposant qu'elles ne se réfèrent pas d'une manière ou d'une autre), l'ordre dans lequel elles sont appelées le fait.

Levon
la source
2
Dans le code que j'ai donné, func_B fait des choses (1) à la copie globale de x (comme obtenu à partir de func_A), (2) à une variable locale x avec la même valeur du résultat de func_A, ou (3) à une variable locale x sans valeur et (aux yeux du compilateur) aucune relation avec "une certaine valeur" ou le x dans func_A?
Akshat Shekhar
xin func_Best une variable locale qui tire sa valeur de la valeur de retour de l'appel à func_A- donc je suppose que cela en ferait votre (2)
Levon
ok, disons que x était une séquence aléatoire quelconque générée par func_A (c'est-à-dire que func_A produisait un x différent à chaque exécution). appelé? Si oui, comment puis-je y remédier?
Akshat Shekhar
1
Oui, si func_Achange la variable globale lors de chaque exécution et la retourne func_Bà utiliser, alors func_Bfonctionnera avec une valeur modifiée à chaque fois. Je ne suis pas sûr de votre "comment y remédier". Vous voudrez peut-être accepter la réponse la plus utile à votre question actuelle / originale, puis envisager d'ouvrir une autre question sur ce qui ressemble à une question de suivi.
Levon
1
En fait, cela dépend de ce que x est. Si x est immuable, alors le x dans func_B y restera, car il est déclaré localement même s'ils ont la même valeur. Cela s'applique aux tuples, aux entiers ... Si c'est une instance d'une liste par exemple et que vous le faites x.append("..."), c'est la variable globale x qui est modifiée, car la locale fait référence à la globale.
jadkik94
111

Dans une étendue Python, toute affectation à une variable non déjà déclarée dans cette étendue crée une nouvelle variable locale à moins que cette variable ne soit déclarée plus tôt dans la fonction comme faisant référence à une variable de portée globale avec le mot clé global.

Regardons une version modifiée de votre pseudocode pour voir ce qui se passe:

# Here, we're creating a variable 'x', in the __main__ scope.
x = 'None!'

def func_A():
  # The below declaration lets the function know that we
  #  mean the global 'x' when we refer to that variable, not
  #  any local one

  global x
  x = 'A'
  return x

def func_B():
  # Here, we are somewhat mislead.  We're actually involving two different
  #  variables named 'x'.  One is local to func_B, the other is global.

  # By calling func_A(), we do two things: we're reassigning the value
  #  of the GLOBAL x as part of func_A, and then taking that same value
  #  since it's returned by func_A, and assigning it to a LOCAL variable
  #  named 'x'.     
  x = func_A() # look at this as: x_local = func_A()

  # Here, we're assigning the value of 'B' to the LOCAL x.
  x = 'B' # look at this as: x_local = 'B'

  return x # look at this as: return x_local

En fait, vous pourriez tout réécrire func_Bavec la variable nommée x_localet cela fonctionnerait de manière identique.

L'ordre n'a d'importance que dans la mesure où vos fonctions effectuent des opérations qui modifient la valeur du x global. Ainsi, dans notre exemple, l'ordre n'a pas d'importance, car les func_Bappels func_A. Dans cet exemple, l'ordre importe:

def a():
  global foo
  foo = 'A'

def b():
  global foo
  foo = 'B'

b()
a()
print foo
# prints 'A' because a() was the last function to modify 'foo'.

Notez que cela globaln'est nécessaire que pour modifier des objets globaux. Vous pouvez toujours y accéder depuis une fonction sans déclarer global. Ainsi, nous avons:

x = 5

def access_only():
  return x
  # This returns whatever the global value of 'x' is

def modify():
  global x
  x = 'modified'
  return x
  # This function makes the global 'x' equal to 'modified', and then returns that value

def create_locally():
  x = 'local!'
  return x
  # This function creates a new local variable named 'x', and sets it as 'local',
  #  and returns that.  The global 'x' is untouched.

Notez la différence entre create_locallyet access_only- access_onlyaccède au x global sans appeler global, et même s'il create_locallyn'utilise pas non globalplus, il crée une copie locale car il attribue une valeur.

La confusion ici est la raison pour laquelle vous ne devez pas utiliser de variables globales.

jdotjdot
la source
2
Je ne pense pas que cela soit très déroutant en pratique, il vous suffit de comprendre les règles de portée de python .
Casey Kuball
20

Comme d'autres l'ont noté, vous devez déclarer une variable globaldans une fonction lorsque vous voulez que cette fonction puisse modifier la variable globale. Si vous voulez seulement y accéder, vous n'en avez pas besoin global.

Pour entrer un peu plus dans les détails, ce que signifie "modifier" est le suivant: si vous voulez lier à nouveau le nom global pour qu'il pointe vers un objet différent, le nom doit être déclaré globaldans la fonction.

De nombreuses opérations qui modifient (mutent) un objet ne lient pas à nouveau le nom global pour pointer vers un autre objet et sont donc toutes valides sans déclarer le nom globaldans la fonction.

d = {}
l = []
o = type("object", (object,), {})()

def valid():     # these are all valid without declaring any names global!
   d[0] = 1      # changes what's in d, but d still points to the same object
   d[0] += 1     # ditto
   d.clear()     # ditto! d is now empty but it`s still the same object!
   l.append(0)   # l is still the same list but has an additional member
   o.test = 1    # creating new attribute on o, but o is still the same object
gentil
la source
8

Voici un cas qui m'a surpris, en utilisant un global comme valeur par défaut d'un paramètre.

globVar = None    # initialize value of global variable

def func(param = globVar):   # use globVar as default value for param
    print 'param =', param, 'globVar =', globVar  # display values

def test():
    global globVar
    globVar = 42  # change value of global
    func()

test()
=========
output: param = None, globVar = 42

Je m'attendais à ce que param ait une valeur de 42. Surprise. Python 2.7 a évalué la valeur de globVar lors de la première analyse de la fonction func. La modification de la valeur de globVar n'a pas affecté la valeur par défaut affectée à param. Le report de l'évaluation, comme ci-dessous, a fonctionné comme j'en avais besoin.

def func(param = eval('globVar')):       # this seems to work
    print 'param =', param, 'globVar =', globVar  # display values

Ou, si vous voulez être en sécurité,

def func(param = None)):
    if param == None:
        param = globVar
    print 'param =', param, 'globVar =', globVar  # display values
SoloPilot
la source
Cela m'a rappelé le problème d' assigner une liste vide comme valeur par défaut . Et, comme dans l'exemple, utilisez ispour vérifier si quelque chose est None, au lieu de la comparaison normale ==.
berna1111
6

Vous pouvez accéder directement à une variable globale à l'intérieur d'une fonction. Si vous souhaitez modifier la valeur de cette variable globale, utilisez "global variable_name". Voir l'exemple suivant:

var = 1
def global_var_change():
      global var
      var = "value changed"
global_var_change() #call the function for changes
print var

D'une manière générale, ce n'est pas une bonne pratique de programmation. En brisant la logique de l'espace de noms, le code peut devenir difficile à comprendre et à déboguer.

Noisy_Botnet
la source
2

Vous devez utiliser la globaldéclaration lorsque vous souhaitez modifier la valeur affectée à une variable globale.

Vous n'en avez pas besoin pour lire à partir d'une variable globale. Notez que l'appel d'une méthode sur un objet (même s'il modifie les données de cet objet) ne modifie pas la valeur de la variable contenant cet objet (en l'absence de magie réfléchissante).

Marcin
la source
2
Cette formulation est regrettable. En Python, la valeur attribuée à une variable est une référence, elle est donc techniquement correcte (et je ne doute pas que vous vouliez dire cela), mais de nombreux lecteurs peuvent interpréter "modifier la valeur" comme "muter l'objet", ce qui n'est pas le cas - xs.append(xs.pop(0))fonctionne très bien sans global xs.
@delnan Ma réponse est soigneusement formulée, mais je vais clarifier.
Marcin