Paikalliset ja globaalit muuttujat
Muuttujan näkyvyysalue (scope) tarkoittaa, missä ohjelman osissa muuttujaa voi käyttää. Paikallinen muuttuja on muuttuja, joka on näkyvissä vain tietyn rajatun alueen sisällä ohjelmassa. Globaali muuttuja on puolestaan käytettävissä missä tahansa ohjelman osassa.
Paikalliset muuttujat
Pythonissa funktion sisällä määritellyt muuttujat ovat funktion paikallisia muuttujia. Tämä koskee sekä parametreja että funktion lohkon sisällä esiteltyjä muuttujia. Paikallisuus tarkoittaa, että muuttuja ei ole olemassa funktion ulkopuolella.
Esimerkiksi seuraavassa ohjelmassa yritys viitata muuttujaan x
pääohjelmassa antaa virheen:
def testi():
x = 5
print(x)
testi()
print(x)
5 NameError: name 'x' is not defined
Ohjelmassa muuttuja x
on siis olemassa vain funktion testi
suorituksen ajan eikä siihen pääse käsiksi muista funktioista tai pääohjelmasta.
Globaalit muuttujat
Pääohjelmassa eli kaikkien funktioiden ulkopuolella määritellyt muuttujat ovat globaaleja muuttujia. Globaalin muuttujan arvo voidaan lukea funktiossa. Esimerkiksi seuraava toimii:
def testi():
print(x)
x = 3
testi()
3
Kuitenkaan globaalia muuttujaa ei voi muuttaa suoraan. Esimerkiksi seuraava funktio ei vaikuta globaaliin muuttujaan:
def testi():
x = 5
print(x)
x = 3
testi()
print(x)
5 3
Tässä tapauksessa funktio testi
luo paikallisen muuttujan x
, joka saa arvon 5. Tämä on kuitenkin eri muuttuja kuin pääohjelmassa oleva muuttuja x
.
Entä miten toimii seuraava koodi?
def testi():
print(x)
x = 5
x = 3
testi()
print(x)
UnboundLocalError: local variable 'x' referenced before assignment
Funktiossa testi
annetaan arvo muuttujalle x
, jolloin Python päättelee, että x
on funktion paikallinen muuttuja (eikä globaali muuttuja). Koska muuttujaan yritetään viitata ennen arvon asettamista, tapahtuu virhe.
Jos kuitenkin haluamme muuttaa funktiossa globaalia muuttujaa, tämä onnistuu avainsanan global
avulla:
def testi():
global x
x = 3
print(x)
x = 5
testi()
print(x)
3 3
Nyt funktiossa tehty muutos x = 3
vaikuttaa myös pääohjelmaan, eli kaikissa ohjelman kohdissa x
viittaa samaan muuttujaan.
Milloin käyttää globaalia muuttujaa?
Globaalien muuttujien tarkoituksena ei ole korvata funktion parametreja tai paluuarvoa. Esimerkiksi on sinänsä mahdollista tehdä seuraava funktio, joka tallentaa laskun tuloksen globaaliin muuttujaan:
def laske_summa(a, b):
global tulos
tulos = a + b
laske_summa(2, 3)
print(tulos)
Parempi tapa on kuitenkin toteuttaa funktio kuten ennenkin:
def laske_summa(a, b):
return a + b
tulos = laske_summa(2, 3)
print(tulos)
Jälkimmäisen tavan etuna on, että funktio on itsenäinen kokonaisuus, jolle annetaan tietyt parametrit ja joka palauttaa tietyn tuloksen. Funktiolla ei ole sivuvaikutuksia, minkä ansiosta sitä voi testata muusta koodista riippumattomasti.
Kuitenkin globaali muuttuja voi olla hyödyllinen, jos halutaan pitää yllä jotain funktioille yhteistä "ylemmän tason" tietoa. Tässä on yksi esimerkki asiasta:
def laske_summa(a, b):
global laskuri
laskuri += 1
return a + b
def laske_erotus(a, b):
global laskuri
laskuri += 1
return a - b
laskuri = 0
print(laske_summa(2, 3))
print(laske_summa(5, 5))
print(laske_erotus(5, 2))
print(laske_summa(1, 0))
print("Funktioita kutsuttiin", laskuri, "kertaa")
Tässä haluamme pitää ohjelman suorituksen aikana kirjaa siitä, montako kertaa funktioita on kutsuttu ohjelman eri kohdista. Nyt globaali muuttuja laskuri
on kätevä, koska voimme kasvattaa sen arvoa jokaisella funktion kutsukerralla ja katsoa globaalista muuttujasta, montako kertaa funktiota on kutsuttu.
Tiedon välittäminen funktiosta toiseen revisited
Jos ohjelma koostuu useista funktioista, nousee esiin kysymys miten tietoa siirretään funktiosta toiseen.
Seuraavassa on jo pari osaa sitten nähty esimerkki ohjelmasta, joka lukee käyttäjältä joukon kokonaislukuarvoja. Sen jälkeen ohjelma tulostaa arvot ja tekee niille vielä "analyysin". Ohjelma on jaettu kolmeen erilliseen funktioon:
def lue_kayttajalta(maara: int):
print(f"syötä {maara} lukua:")
luvut = []
i = maara
while i>0:
luku = int(input("anna luku: "))
luvut.append(luku)
i -= 1
return luvut
def tulosta(luvut: list):
print("luvut ovat: ")
for luku in luvut:
print(luku)
def analysoi(luvut: list):
ka = sum(luvut) / len(luvut)
return f"lukuja yhteensä {len(luvut)}, keskikarvo {ka}, pienin {min(luvut)} ja suurin {max(luvut)}"
# funktioita käyttävä "pääohjelma"
syotteet = lue_kayttajalta(5)
tulosta(syotteet)
analyysin_tulos = analysoi(syotteet)
print(analyysin_tulos)
Esimerkkisuoritus
syötä 5 lukua: anna luku: 10 anna luku: 34 anna luku: -32 anna luku: 99 anna luku: -53 luvut ovat: 10 34 -32 99 -53 lukuja yhteensä 5, keskikarvo 11.6, pienin- 53 ja suurin 99
Perusperiaatteena ohjelmassa on se, että pääohjelma "tallettaa" ohjelman käsittelemän tiedon, eli tässä tapauksessa käyttäjän syöttämät luvut muuttujassa syotteet
.
Jos lukuja on tarve käsitellä jossain funktiossa, ne välitetään sinne parametrina. Näin tapahtuu funktioissa tulosta
ja analysoi
.
Jos taas funktio tuottaa tietoa, jota muut ohjelman osat tarvitsevat, palauttaa funktio datan returnilla. Näin tekevät käyttäjän syötteen lukeva funktio lue_kayttajalta
sekä analyysin tekevä funktio analysoi
.
Olisi periaatteessa mahdollista, että funktiot käyttäisivät avainsanaa global
hyväksikäyttäen suoraan "pääohjelman" globaalia muuttujaa syotteet
. Se ei kuitenkaan ole ollenkaan järkevää, sillä jos usea funktio pääsee sorkkimaan globaalia muuttujaa, voi ohjelmassa alkaa tapahtua jotain hallitsematonta, varsinkin kun funktioiden määrä kasvaa.
Tiedon välitys funktioihin ja niistä ulos on siis järkevintä hoitaa parametrien ja paluuarvojen avulla.
Jos haluaisimme tehdä edellisen esimerkin ohjelman siten, että sen "pääohjelma" eriytettäisiin omaan funktioon main
, siirrettäisiin ohjelman käsittelemä data pääohjelmaa edustavan funktion sisäiseksi muuttujaksi:
def lue_kayttajalta(maara: int):
print(f"syötä {maara} lukua:")
luvut = []
i = maara
while i>0:
luku = int(input("anna luku: "))
luvut.append(luku)
i -= 1
return luvut
def tulosta(luvut: list):
print("luvut ovat: ")
for luku in luvut:
print(luku)
def analysoi(luvut: list):
ka = sum(luvut) / len(luvut)
return f"lukuja yhteensä {len(luvut)} keskikarvo {ka} pienin{min(luvut)} ja suurin {max(luvut)}"
# pääohjelmaa edustava funktio
def main():
syotteet = lue_kayttajalta(5)
tulosta(syotteet)
analyysin_tulos = analysoi(syotteet)
print(analyysin_tulos)
# ohjelman käynnistys
main()
Log in to view the quiz
Vastaa lopuksi osion loppukyselyyn:
Log in to view the quiz