11: Lokalūs ir globalūs kintamieji

Programos yra skaidomos į dalis, kad būtų lengviau tvarkomos, pvz, į keils failus, arba faile kodas būna suskaidytas į  funkcijas. Skirtingose programos dalyse gali pasitaikyti vienodai pavadintų kintamųjų (kaip kad skirtingose šeimose būna vaikų tais pačiais vardais) - jie tarpusavy nesimaišo, nes naudojami tik tos dalies viduje - lokaliai. O kai kurie kintamieji gali būti žinomi visoje programoje - globaliai (kaip kokie visuomenės veikėjai ar pop. žvaigždės).

Kintamųjų galiojimo sritis

def square(x):
   value = x * x
   return value
Ši funkcija skaičiuoja skaičiaus x kvadratą. Tarkime, kad funkciją square iškviesime iš programos, kurioje taip pat yra value kintamasis, tačiau naudojamas kitais tikslais.

# pagrindinė programa
value = 17
penki_kvadratu = square(5)
print(penki_kvadratu, value)
Kaip manote, kas bus atspausdinta? Nežinodami kaip Pitonas elgsis, galime svarstyti dvi galimybes:

  1. Arba Pitone yra du atskiri value kintamieji (vienas funkcijoje "square", kitas - už jos ribų ). Taigi, square grąžins 25, bet value reikšmė pagrindinėje programoje išliks 17. Bus atspausdinama: 25 17.
  2. Arba visur yra tas pats kintamasis value. Funkcija square pakeičia value reikšmę, taigi programa atspausdins: 25 25.

O kaip yra iš tikro?

Matome, jog Pitonas veikia pagal pirmąjį variantą.  Vykdant funkciją, Pitonas nepakeičia kintamųjų, esančių už jos ribų. Priskyrimo veiksmai pakeičia tik “lokalius" kintamuosius - esančius funkcijos viduje. Tai natūraliai apsaugo nuo netikėtų nesusipratimų (pvz., jei kelios funkcijos naudotų tą patį kintamojo vardą).

Detaliau tai galite pamatyti 6-tame žingsnyje ("Step 6 of 8"): matosi du skirtingi kintamieji value: vienas square funkcijoje, o kitas už funkcijos ribų - “globalioje” programos srityje.

Pasirinkimo užduotis: Vidus ir išorė
Ką atspausdins ši programa?

x = "outer"
def xReplace(value):
   x = value

xReplace("inner")
print(x)
Teisingai! Šis sakinys/teiginys x = value veikia tiktai viduje esantį kintamajį x. Išorėje esantis globalus kintamasis x nebuvo pakeistas. (taigi, funkcija xReplace yra bevertė.)

Į kintamųjų galijimo sriį ("variable scope") panaši sąvoka yra "namespace" (vardų galiojimo sritis). Jos pagalba nurodoma, kad kintamasis yra iš konkrečios programos dalies -- iš kažkurio importuoto modulio. Pvz., jei importuojame paketą  math, kuriame yra kintamąsis  x, jis nepersidengia su mūsų programos kintamuoju x, nes pasiekiamas „per tašką“ math.x

Sričių taisyklė: pirmiau žiūrim viduje, o paskui - išorėn

Kartais prireikia funkcijos viduje panaudoti globalius kintamuosius. Pvz., programos pradžioje priskiriame kintamajam reikšmę, ir norime, kad mūsų funkcijos ją žinotų - matytų.

Pavyzdys
Kaip iš funkcijos perskaityti globalų kintamajį?

Funkcijoje nebuvo kintamojo favouriteTopping, tačiau klaida neįvyko. Pitonas kintamuosius peržiūri pagal tokią tvarką: visų pirma ieško lokalaus kintamojo, o po to -- globaliai galiojančio. Mūsų atveju programa rado favouriteTopping globalioje srityje.

Aukščiau esantys pavyzdžiai su orderPizzas ir xReplace yra beveik identiški. Kodėl  xReplace kintamasis x yra lokalus, o orderPizzas atveju favouriteTopping - lieka globalus? Pitone galioja taisyklė: jei funkcijoje kintamajam priskiriama reikšmė, tai jis standartiškai padaromas lokaliu (kad nebūtų konfliktų su kitom programos dalimis). Tai pati dažniausia priežastis klaidos: UnboundLocalError: daugiau apie tai [1, 2] oficialioje Pitono dokumentacijoje.

Funkcijos argumentai yra visada laikomi naujais lokaliais kintamaisiais. Tai pailiustruoja šis neveikiantis kodas ( jis bando kintamąjam g priskirti reikšmę 0).

Situacija su kintamųjų galiojimo sritimi galima palyginti su gyventojų vieša ir privačia erdve: tai, kas vieša (globalu) - žinoma visiems, o kas privatu (lokalu) - nežinoma už namo sienų.

global'ūs pakeitimai

Aukščiau aprašytas princiapas tinka 99% atvejų. Tačiau, kartais patogu globalų kintamąjį pakeisti funkcijos viduje. Tai galima padaryti naudojant raktažodį global .

Štai aukščiau buvusio xReplace modifikacija: nurodžius, kad kintamasis yra global'us, jo reikšmė pakeičiama iš "outer" į "inner":

Kaip jau minėjome, norint panaudoti globalaus kintamojo reikšmę, nereikia naudoti global. Jis naudojamas tik norint priskirti/pakeisti reikšmes.

P.s.: 13 pamokoje išmoksite apie sąrašus -- jų pertvarkymus funkcijojse galima daryti be global, ir jie išliks, nes sąrašų saugojimas atmintyje įdomesnis negu paprastų kintamųjų ;).