Variabili globali della funzione Python?


272

So che dovrei evitare di usare le variabili globali in primo luogo a causa di confusione come questa, ma se dovessi usarle, è il seguente un modo valido per usarle? (Sto cercando di chiamare la copia globale di una variabile creata in una funzione separata.)

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()

Quello xche utilizza la seconda funzione ha lo stesso valore della copia globale xche func_autilizza e modifica? Quando si chiamano le funzioni dopo la definizione, l'ordine conta?


1
fai anche attenzione a non dare per scontato solo perché nella tua funzione è stata assegnata una variabile che Python tratterà i riferimenti prima dell'assegnazione come tale. Fino al primo incarico, se si utilizzava x, non sarebbe quello globale o quello locale. Otterrai la famigerata eccezione UnboundLocalError in faccia :)
osirisgothra,

Risposte:


412

Se vuoi semplicemente accedere a una variabile globale, basta usare il suo nome. Tuttavia, per modificarne il valore è necessario utilizzare la globalparola chiave.

Per esempio

global someVar
someVar = 55

Ciò cambierebbe il valore della variabile globale in 55. Altrimenti assegnerebbe 55 a una variabile locale.

L'ordine degli elenchi di definizioni delle funzioni non ha importanza (supponendo che non si riferiscano l'uno all'altro in qualche modo), l'ordine in cui vengono chiamati lo fa.


2
Nel codice che ho dato, func_B sta facendo cose (1) nella copia globale di x (come ottenuto da func_A), (2) in una variabile locale x con lo stesso valore del risultato di func_A, oppure (3) in una variabile locale x senza valore e (agli occhi del compilatore) nessuna relazione con "un valore" o la x in func_A?
Akshat Shekhar,

xin func_Bè una variabile locale che ottiene il suo valore dal valore restituito della chiamata a func_A- quindi immagino che lo renderebbe tuo (2)
Levon

ok, diciamo che x era una sequenza casuale di qualche tipo generata da func_A (cioè che func_A produceva una x diversa ogni volta che veniva eseguita.) Avere eseguito il programma come scritto farebbe a func_b modificare una x diversa da quella originariamente prodotta quando func_a era chiamato? In tal caso, come posso ripararlo?
Akshat Shekhar,

1
Sì, se func_Acambia la variabile globale durante ogni esecuzione e la restituisce per func_Bl'uso, func_Bfunzionerà ogni volta con un valore modificato. Non sono sicuro del tuo "come risolverlo". Potresti voler accettare la risposta più utile alla tua domanda attuale / originale e quindi prendere in considerazione l'idea di aprire una domanda diversa su ciò che sembra una domanda di follow-up.
Levon,

1
In realtà dipende da cosa è x. Se x è immutabile, allora x in func_B rimarrà in esso, perché è dichiarato localmente anche se hanno lo stesso valore. Questo vale per tuple, ints ... Se ad esempio è un'istanza di un elenco e lo fai x.append("..."), è la variabile globale x che viene modificata, perché quella locale fa riferimento a quella globale.
jadkik94,

111

All'interno di un ambito Python, qualsiasi assegnazione a una variabile non già dichiarata all'interno di tale ambito crea una nuova variabile locale a meno che tale variabile non sia stata dichiarata in precedenza nella funzione come riferita a una variabile con ambito globale con la parola chiave global.

Diamo un'occhiata a una versione modificata del tuo pseudocodice per vedere cosa succede:

# 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

In effetti, è possibile riscrivere tutto func_Bcon la variabile denominatax_local e funzionerebbe in modo identico.

L'ordine conta solo fino all'ordine in cui le tue funzioni eseguono operazioni che cambiano il valore della x globale. Pertanto, nel nostro esempio, l'ordine non ha importanza, poiché le func_Bchiamate func_A. In questo esempio, l'ordine conta:

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'.

Si noti che globalè richiesto solo per modificare oggetti globali. È comunque possibile accedervi dall'interno di una funzione senza dichiarare global. Pertanto, abbiamo:

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.

Nota la differenza tra create_locallye access_only- access_onlysta accedendo alla x globale nonostante non chiami global, e anche se create_locallynon usa globalneanche, crea una copia locale poiché sta assegnando un valore.

La confusione qui è perché non dovresti usare le variabili globali.


2
Non penso che questo sia molto confuso in pratica, devi solo capire le regole di scoping di Python .
Casey Kuball,

20

Come altri hanno già notato, è necessario dichiarare una variabile globalin una funzione quando si desidera che tale funzione sia in grado di modificare la variabile globale. Se si desidera solo accedervi, non è necessario global.

Per entrare un po 'più in dettaglio su ciò, ciò che significa "modifica" è questo: se si desidera ricollegare il nome globale in modo che punti a un oggetto diverso, il nome deve essere dichiarato globalnella funzione.

Molte operazioni che modificano (mutano) un oggetto non ricollegano il nome globale per puntare a un oggetto diverso, e quindi sono tutte valide senza dichiarare il nomeglobal nella funzione.

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

8

Ecco un caso che mi ha sorpreso, usando un valore globale come valore predefinito di un parametro.

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

Mi aspettavo che param avesse un valore di 42. Sorpresa. Python 2.7 ha valutato il valore di globVar quando ha analizzato per la prima volta la funzione func. La modifica del valore di globVar non ha influito sul valore predefinito assegnato a param. Ritardare la valutazione, come nel seguito, ha funzionato come avevo bisogno.

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

Oppure, se vuoi essere al sicuro,

def func(param = None)):
    if param == None:
        param = globVar
    print 'param =', param, 'globVar =', globVar  # display values

Ciò mi ha ricordato il problema di assegnare un elenco vuoto come valore predefinito . E, come nell'esempio, usa isper verificare se c'è qualcosa None, invece del normale confronto ==.
berna1111

6

È possibile accedere direttamente a una variabile globale all'interno di una funzione. Se si desidera modificare il valore di quella variabile globale, utilizzare "nome_variare globale". Vedi il seguente esempio:

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

In generale, questa non è una buona pratica di programmazione. Rompendo la logica dello spazio dei nomi, il codice può diventare difficile da capire e da eseguire nel debug.


2

È necessario utilizzare la globaldichiarazione quando si desidera modificare il valore assegnato a una variabile globale.

Non è necessario per leggere da una variabile globale. Nota che chiamare un metodo su un oggetto (anche se altera i dati all'interno di quell'oggetto) non altera il valore della variabile che contiene quell'oggetto (assenza di magia riflettente).


2
Questa formulazione è sfortunata. In Python, il valore assegnato a una variabile è un riferimento, quindi è tecnicamente corretto (e non ho dubbi sul fatto che lo intendessi), ma molti lettori possono interpretare "alterare il valore" come "mutare l'oggetto", che non è il caso - xs.append(xs.pop(0))funziona bene senza global xs.

@delnan La mia risposta è formulata con cura, ma chiarirò.
Marcin,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.