Zmienne globalne funkcji Pythona?

271

Wiem, że powinienem unikać używania zmiennych globalnych przede wszystkim z powodu takiego zamieszania, ale jeślibym ich używał, czy przestrzeganie tych zasad jest dobrym sposobem? (Próbuję wywołać globalną kopię zmiennej utworzonej w oddzielnej funkcji).

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

Czy xdruga funkcja używa tej samej wartości globalnej kopii, xktóra func_aużywa i modyfikuje? Czy przy wywoływaniu funkcji po zdefiniowaniu kolejność ma znaczenie?

Akshat Shekhar
źródło
1
bądź ostrożny, aby nie zakładać, że masz przypisaną zmienną w swojej funkcji, że Python będzie traktował referencje przed przypisaniem jako takie. Do pierwszego przypisania, jeśli użyjesz x, nie będzie to globalne ani lokalne. Otrzymasz niesławny wyjątek UnboundLocalError :)
osirisgothra

Odpowiedzi:

412

Jeśli chcesz po prostu uzyskać dostęp do zmiennej globalnej, po prostu użyj jej nazwy. Jednak, aby zmienić jego wartość, musisz użyć globalsłowa kluczowego.

Na przykład

global someVar
someVar = 55

Zmieniłoby to wartość zmiennej globalnej na 55. W przeciwnym razie po prostu przypisałoby 55 do zmiennej lokalnej.

Kolejność list definicji funkcji nie ma znaczenia (zakładając, że nie odnoszą się one do siebie w jakiś sposób), kolejność, w jakiej są nazywane, ma znaczenie.

Levon
źródło
2
W kodzie, który podałem, func_B robi rzeczy (1) z globalną kopią x (otrzymaną z func_A), (2) do lokalnej zmiennej x o tej samej wartości wyniku func_A, lub (3) do zmienna lokalna x bez wartości i (w oczach kompilatora) brak związku z „jakąś wartością” lub x w func_A?
Akshat Shekhar
xin func_Bjest zmienną lokalną, która pobiera swoją wartość z wartości zwracanej wywołania do func_A- więc myślę, że byłaby to twoja (2)
Levon
ok, powiedzmy, że x był przypadkową sekwencją generowaną przez func_A (tj. że func_A produkował inny x za każdym razem, gdy był uruchamiany). Uruchamianie programu tak, jak napisano, modyfikowałoby func_b inne x niż to, co pierwotnie powstało, gdy func_a była nazywa? Jeśli tak, jak mogę to naprawić?
Akshat Shekhar
1
Tak, jeśli func_Azmieni globalną zmienną podczas każdego uruchomienia i przywróci ją func_Bdo użycia, func_Bbędzie działać za każdym razem ze zmienioną wartością. Nie jestem pewien twojego „jak to naprawić”. Możesz zaakceptować najbardziej pomocną odpowiedź na bieżące / oryginalne pytanie, a następnie rozważyć otwarcie innego pytania o to, co wygląda na pytanie uzupełniające.
Levon
1
Właściwie to zależy od tego, czym jest x. Jeśli x jest niezmienne, to x w func_B pozostanie w nim, ponieważ jest deklarowane lokalnie, nawet jeśli mają tę samą wartość. Odnosi się to do krotek, ints ... Jeśli jest to na przykład instancja listy, a Ty tak x.append("..."), zmieniona jest zmienna globalna x, ponieważ lokalna odwołuje się do globalnej.
jadkik94
110

W zakresie Pythona każde przypisanie do zmiennej, która nie została jeszcze zadeklarowana w tym zakresie, tworzy nową zmienną lokalną, chyba że zmienna ta została zadeklarowana wcześniej w funkcji jako odnosząca się do zmiennej o zasięgu globalnym ze słowem kluczowym global.

Spójrzmy na zmodyfikowaną wersję twojego pseudokodu, aby zobaczyć, co się stanie:

# 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

W rzeczywistości możesz przepisać wszystko func_Bze zmienną o nazwie x_locali będzie działać identycznie.

Kolejność ma znaczenie tylko w takim stopniu, w jakim twoje funkcje wykonują operacje zmieniające wartość globalnego x. Zatem w naszym przykładzie kolejność nie ma znaczenia, ponieważ func_Bpołączenia func_A. W tym przykładzie kolejność ma znaczenie:

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

Pamiętaj, że globaljest to wymagane tylko do modyfikowania obiektów globalnych. Nadal możesz uzyskać do nich dostęp z poziomu funkcji bez deklarowania global. Mamy zatem:

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.

Zwróć uwagę na różnicę między create_locallyi access_only- access_onlyuzyskuje dostęp do globalnego x, mimo że nie dzwoni global, i chociaż create_locallynie używa globalżadnego z nich, tworzy lokalną kopię, ponieważ przypisuje wartość.

To zamieszanie powoduje, że nie powinieneś używać zmiennych globalnych.

jdotjdot
źródło
2
Nie sądzę, że jest to bardzo mylące w praktyce, wystarczy zrozumieć zasady określania zakresu Pythona .
Casey Kuball
20

Jak zauważyli inni, musisz zadeklarować zmienną globalw funkcji, jeśli chcesz, aby ta funkcja mogła modyfikować zmienną globalną. Jeśli chcesz tylko uzyskać do niego dostęp, nie potrzebujesz global.

Aby przejść do bardziej szczegółowych informacji na temat tego, co oznacza „modyfikuj”, jest to: jeśli chcesz ponownie powiązać nazwę globalną, aby wskazywała na inny obiekt, nazwa musi zostać zadeklarowana globalw funkcji.

Wiele operacji, które modyfikują (mutują) obiekt , nie wiążą ponownie globalnej nazwy w celu wskazania innego obiektu, a zatem wszystkiepoprawne bez podania nazwy globalw funkcji.

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
kindall
źródło
8

Oto jeden przypadek, który mnie złapał, używając globalnej jako domyślnej wartości parametru.

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

Spodziewałem się, że param będzie miał wartość 42. Niespodzianka. Python 2.7 ocenił wartość globVar podczas pierwszej analizy funkcji func. Zmiana wartości globVar nie wpłynęła na domyślną wartość przypisaną do parametru. Opóźnianie oceny, jak poniżej, działało tak, jak tego potrzebowałem.

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

Lub, jeśli chcesz być bezpieczny,

def func(param = None)):
    if param == None:
        param = globVar
    print 'param =', param, 'globVar =', globVar  # display values
SoloPilot
źródło
Przypomniało mi to problem przypisania pustej listy jako wartości domyślnej . I, jak w przykładzie, użyj, isaby sprawdzić, czy coś jest None, zamiast zwykłego porównania ==.
berna1111
6

Możesz bezpośrednio uzyskać dostęp do zmiennej globalnej wewnątrz funkcji. Jeśli chcesz zmienić wartość tej zmiennej globalnej, użyj „globalnej nazwy zmiennej”. Zobacz następujący przykład:

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

Ogólnie rzecz biorąc, nie jest to dobra praktyka programowania. Łamanie logiki przestrzeni nazw powoduje, że kod może być trudny do zrozumienia i debugowania.

Noisy_Botnet
źródło
2

Z globaldeklaracji należy skorzystać , aby zmienić wartość przypisaną zmiennej globalnej.

Nie potrzebujesz go do odczytu ze zmiennej globalnej. Zauważ, że wywołanie metody na obiekcie (nawet jeśli zmienia dane w tym obiekcie) nie zmienia wartości zmiennej przechowującej ten obiekt (brak magii odbijającej).

Marcin
źródło
2
To sformułowanie jest niefortunne. W Pythonie wartość przypisana do zmiennej jest odwołaniem, więc jest technicznie poprawna (i nie mam wątpliwości, że miałeś na myśli to), ale wielu czytelników może interpretować „zmienić wartość” jako „mutować obiekt”, co nie jest obudowa - xs.append(xs.pop(0))działa dobrze bez global xs.
@delnan Moja odpowiedź jest starannie sformułowana, ale wyjaśnię.
Marcin