Osa 6

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

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

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

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

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

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")
Esimerkkitulostus
5 10 3 1 Funktioita kutsuttiin 4 kertaakertaa

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

Esimerkkitulostus

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()
Loading...
:
Loading...

Log in to view the quiz

Vastaa lopuksi osion loppukyselyyn:

Loading...
:
Loading...

Log in to view the quiz