Loeng 6: Lahti- ja kokkupakkimine ning operaatorid =, *, **, muutujate skoop, LEGB reegel, lausendid global ja nonlocal, for-in avaldis, for-in-if avaldis, if-else-for-in avaldis, if-else-for-in-if avaldis¶

Viimati uuendatud 5.10.2025.

Koodinäited¶

1  Omistusoperaator =¶

1.1  Paralleelne muutujatele väärtuste omistamine (tuple assignment)¶

Meeldetuletuseks, omistusoperaator = omitab väärtusi samaaegselt mitmele muutujale järgmiselt:

In [1]:
a, b, c = 1, 'Juulius', 3  # Võrdne arv muutujanimesid ja väärtusi.
print(a, b, c)
1 Juulius 3

Siin vasak pool on korteež muutuja nimedest ja paremal pool on omistatavad väärtused.

1.2  Operaator = ja iteraatortoega andmetüübi objektid¶

Omistusoperaator = pakib lahti iteraatortoega andmetüüpide objekte:

In [2]:
a, b, c = (1, 2, 3)  # Näeb sisse. Proovi listiga, hulgaga, jne.
print(a, b, c)
1 2 3
In [3]:
my_dict = {'üks': 1, 'kaks': 2, 'kolm': 3}
a, b, c = my_dict  # Proovi ka sõnastiku meetodeid items ja values.
print(a, b, c) 
üks kaks kolm

Meie jaoks uus käitumine. Python toetab nii vasakul kui ka paremal pool omistusoperaatorit = oleva iteraatortoega objekti lahtipakkimist.

In [4]:
[a, b, c] = 1, 2, 3  # Mõlemapoolne lahtipakkimine toetatud.
print(a, b, c)
1 2 3
In [5]:
(a, b, c) = [1, 2, 3]  # Mõlemapoolne lahtipakkimine toetatud.
print(a, b, c)
1 2 3
In [6]:
a, b, c = range(3)  # Vahemik range on iteraatortoega.
print(a, b, c)
0 1 2
In [7]:
a, b, c = 'ABC'
print(a, b, c)
A B C

2  Lahtipakkimise ja kokkupakkimise operaatorid * ja **¶

Kasutame selleks, et laiendada operaatorite = ja in kasutusega seotud võimalusi. Allpool näeme ka selle operaatori rakendust funktsioonide argumentide sulgudes.

2.1  Operaator *¶

Operaator * on iseenda pöördoperaator. Operaatoriga saame väärtusi ka kokku pakkida lisaks lahtipakkimisele:

In [8]:
a = [1, 2, 3]   # Proovi ka teisi andmetüüpe.
print(*a)       # Lahtipakkimine.

print(*[4, 5])  # Lahtipakkimine.
1 2 3
4 5
In [9]:
a, *b = 1, 2, 3  # Kokkupakkimine muutujasse b.
print(a, b)
1 [2, 3]
In [10]:
*a, b = 1, 2, 3  # Kokkupakkimine muutujasse a.
print(a, b)
[1, 2] 3
In [11]:
*a, b, c = 1, 2, 3
print(a, b, c)
[1] 2 3
In [12]:
[*r] = range(5)
r
Out[12]:
[0, 1, 2, 3, 4]
In [13]:
[*'Python']  # Lahtipakkimine loendisse.
Out[13]:
['P', 'y', 't', 'h', 'o', 'n']
In [14]:
{*'Python'}  # Lahtipakkimine hulka.
Out[14]:
{'P', 'h', 'n', 'o', 't', 'y'}
In [15]:
[*range(5)]
Out[15]:
[0, 1, 2, 3, 4]
In [16]:
print(*range(5))
0 1 2 3 4

Kui soovid lahti pakkida jada andmetüüpi ja omistada lahtipakitud objektid muutujale pead kasutama korteeži. Loe lisaks PEP 3132.

In [17]:
*s, = 'Python'  # Sõne lahtipakkimine muutujasse r.
s
Out[17]:
['P', 'y', 't', 'h', 'o', 'n']
In [18]:
*r, = range(5)  # Vahemiku lahtipakkimine muutujasse r.
r
Out[18]:
[0, 1, 2, 3, 4]

2.1.1  Kasutamise näited¶

Alamjadade defineerimine.

In [19]:
seq = [1, 2, 3, 4, 5]
esimene, *sisu, viimane = seq
print(sisu)  # Esimene ja viimane element eemaldatud.
[2, 3, 4]
In [20]:
a, b, *_ = 1, 2, 0, 0, 0, 0  # Muutujanimega _ vihjame, et ei soovi seda edaspidi kasutada.
print(a, b)
print(_)
1 2
[0, 0, 0, 0]

Funktsiooni väljastatud tulemustega töötamine.

In [21]:
def powers(num):
    return num, num**2, num**3

*_, cube = powers(2)
print(cube)  # Oleme ainult kuubist huvitatud.
print(_)
8
[2, 4]

Itereerimine üle alamjadade.

In [22]:
for esimene, *muud in [(1, 2, 3), (4, 5, 6, 7)]:
    print("Esimene:", esimene)
    print("Muud:", muud)
Esimene: 1
Muud: [2, 3]
Esimene: 4
Muud: [5, 6, 7]

Jadade defineerimine.

In [23]:
my_tuple = (1, 2)
my_list = [True, False, None]
my_set = {1, 2}
my_str = "12"

[*my_set, *my_list, *my_tuple, *range(3), *my_str]  # Vt. ka all.
Out[23]:
[1, 2, True, False, None, 1, 2, 0, 1, 2, '1', '2']

Sama tulemus kasutades palju tülikamat loendite konkatenatsiooni.

In [24]:
list(my_set) + my_list + list(my_tuple) + list(range(1, 4)) + list(my_str)
# Palju tülikam.
Out[24]:
[1, 2, True, False, None, 1, 2, 1, 2, 3, '1', '2']

Kasutus funktsiooni argumendi sulgudes. Jadade lahtipakkimine funktsiooni väljakutse sulgudes.

In [25]:
def my_sum(a, b, c):
    return a + b + c

lst = [1, 2, 3]
my_sum(*lst)  # Listi lahtipakkimine.
Out[25]:
6

2.2  Operaator **¶

Operaator ** on sõnastike lahtipakkimise operaator. Allpoole kasutame seda ka funktsioonide argumentide sulgudes.

In [26]:
d1 = dict(a = 1, b = 2)
d2 = {'c2':3, 'd':4}
d3 = {5: 'Python', 6: 'Juulius'}

{**d1, **d2, **d3}  # Defineerib uue sõnaraamatu.
Out[26]:
{'a': 1, 'b': 2, 'c2': 3, 'd': 4, 5: 'Python', 6: 'Juulius'}
In [27]:
d1 = dict(a = 1, b = 2)  # Võti a kordub.
d2 = {'a':4, 'd':5}    # Võti a kordub.

{**d1, **d2}
Out[27]:
{'a': 4, 'b': 2, 'd': 5}

Pane tähele, luues uut sõnaraamtut kirjutas Pythoni interpretaator eelmises koodirakus võtmesõnale a vastava esmalt määratud väärtuse 1 üle väärtusega 4 kuna hulk salvestab ainult unikaalseid objekte. Nagu ikka, koodi interpreteeritakse ülevalt alla ja vasakult paremale. Järjekord loeb!

Operaatoriga * saab lahti pakkida sõnastiku, siis tagastatakse ainul võtmed.

In [28]:
[*{'a':4, 'b':2}]
Out[28]:
['a', 'b']

3  Funktsiooni argumendid ning operatorid * ja **¶

3.1  Argumentide tüübid¶

Siiamaani oleme kasutanud nn. tavalisi ehk positsionaalseid argumente. Lisaks teame, et muutujad mis on funktsiooni koodiblokist väljaspool defineeritud on bloki sees kasutatavad.

Pythoni funktsioonides eristame nelja tüüpi argumente:

  • Tavalised argumendid, positsionaalsed argumendid (nii nagu me mõistame neid tuginedes eelnevatele loengutele) — (standard arguments, positional arguments)
  • *args argumendid, etteteadmata arv positsionaalseid argumente, meelevaldne arv positsionaalseid argumente — (non-keyworded, positional, variable-length arguments)
  • Nimelised või võtmesõnaga argumendid, mitte-positsionaalsed vaikimisiväärtustega argumendid
  • **kwargs argumendid ehk nimelised või võtmesõnaga argumendid, mitte-positsionaalsed etteteadmata arv vaikimisiväärtustega argumendid — (keyworded, non-positional, variable-length arguments)

Muutujanimede arg ja kwargs asemel saad kasutada meelevaldseid nimesid. Tundmatu arvuga argumentide puhul on traditsiooniks kujunenud kasutada just neid nimesid.

3.1.1  Etteteadmata arv positsionaalseid argumente, *args¶

Mõnikord me ei tea mitu argumendi väärtust soovime funktsioonile edasi anda. Kõik mis allpool järgneb kehtib nii blokis def kui ka anonüümses funktsioonis, mida defineeriti kasutades lausendit lambda. Siin kontekstis operaator * pakib argumendid korteeži.

In [29]:
def my_sum(*args):  # Meelevaldne arv argumente. Arvude 1, 2, 3 kokkupakkimine.
    print(type(args))  # Operaator * pakkis argumendid ennikusse.
    summa = 0
    for i in args:  # Itereerin üle enniku.
        summa += i
    return summa

my_sum(1, 2, 3)  # Proovi erinev arv argumente.
<class 'tuple'>
Out[29]:
6
In [30]:
my_sum()  # NB! Argumente saab ka mitte sisestada.
<class 'tuple'>
Out[30]:
0
In [31]:
def my_sum(*args): 
    return sum(args)  # Sisseehitatud funktsioon sum itereerib üle iteraatortoega enniku.

my_sum(1, 2, 3)
Out[31]:
6

Vajadusel saame muutujaid ka lahti pakkida. Operaatori * kasutus funktsiooni väljakutses.

In [32]:
def my_sum(*args): 
    return sum(args)

print(my_sum(*(1, 2, 3)))  # Pakin lahti selleks, et funktsioon saaks kokku pakkida.
print(my_sum(*{1, 2, 3}))
6
6
In [33]:
def my_sum(a, b, c): 
    return a + b + c

arvud = [1, 2, 3]
my_sum(*arvud)  # Pakime listi lahti.
Out[33]:
6

Erinevat tüüpi argumendid sisestame järgmises järjekorras. Esmalt sisesta fikseeitud arvuga positsionaalsed arguendid ja alles siis etteteadmata arvuga argumendid kasutades operaatorit * kujul *args:

In [34]:
def func(a, b, *args):  # Järjekord on oluline.
    print(a)
    print(b)
    print(args)

func(1, 2, 3, 4)
1
2
(3, 4)

3.1.2  Vaikimisiväärtustega argumendid¶

Argumendile saame omistada vaikimisiväärtusi positsionaalselt ja/või viidata neile nimepidi mitte-positsionaalselt (non-positional), ainult siis kui neile ei järgne teist tüüpi argumendid. Pea meeles, et vaikimisiväärtustega argumendid järgnevad positsionaalsetele argumentidele ja *args tüüpi argumentidele funktsiooni argumendi sulgudes.

Vaikimisiväärtustega argumentidele pole kohustuslik väärtusi edastada kuna neil on väärtus vaikimisi olemas.

In [35]:
def test(a, b=12, c=13):  # Järjekord on oluline.
    print(a)
    print(b)
    print(c)
    
test(11, 5)  # Vaikimisi väärtusi pole kohustuslik siin edastada.
11
5
13
In [36]:
def test(a=2, b=12, c=13):
    print(a)
    print(b)
    print(c)
    
test()
2
12
13

Funktsiooni väljakutsumisel saab kasutada võtmesõnu (all argumendid a, b ja c). Kuna kasutad võtmesõnu siis järjekord pole oluline.

In [37]:
def test(a=2, b=12, c=13):
    print(a)
    print(b)
    print(c)
                # Viitan nimepidi, mitte-positsionaalselt:   
test(b=7, a=6)  # Väärtuste muutmine ja ülekirjutamine ei sõltu siin asukohast.
6
7
13
In [38]:
def test(a, b, c):
    print(a)
    print(b)
    print(c)
                      # Viitan nimepidi, mitte-positsionaalselt:   
test(c=13, b=7, a=6)  # Väärtuste edastamine ei sõltu siin asukohast.
6
7
13
In [39]:
f = lambda x, y=12: x + y
f(2)
Out[39]:
14

Vaikeväärtusi saab defineerida ka funktsiooni väljakutses.

In [40]:
def func(a, *args, x=55, y, z):  # NB! y ja z on võtmesõnaga argumenid millele pole siin vaikimisiväärtusi omistatud.
    print(a)
    print(args)
    print('x =', x)
    print('y =', y)
    print('z =', z)
        
func(1, 2, 3 ,4, 5, 6, 7, y=33, z=44)  # Peame vaikimisiväärtused siin edastama.
1
(2, 3, 4, 5, 6, 7)
x = 55
y = 33
z = 44

Vaikimisiväärtusega argumendid on meile juba tuttavad. Vaikimisiväärtusega argumendi kasutamine sisseehitatud funktsioonis print. Vaikimisi sisestab funktsioon print uue rea peale väljatrüki teostamist. Seda käitumist kontrollib nimega argument end, vaikimisi omab see väärtust \n:

In [41]:
print('Python', end='\n')  # Muutujanime end vaikeväärtus on reavahetus.
Python

Muudame nimega argumendi väärtust:

In [42]:
print('Python', end=' Juulius Tipikas')
Python Juulius Tipikas

Märkus: Operaator * ei suuda kokku pakkida vaikeväärtusega argumente.

In [43]:
def print_all(*args):
    print(args)

print_all(1, 2, x=3)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[43], line 4
      1 def print_all(*args):
      2     print(args)
----> 4 print_all(1, 2, x=3)

TypeError: print_all() got an unexpected keyword argument 'x'

3.1.3  Positsionaalsed ja vaikeväärtustega argumendid, lubatud süntaksi kuju¶

Lisaks eelmainitud sünkaksinäidetele kehtib ka järgmine kirjapilt.

Allolevas näites:

  • x ja y on positsionaalsed argumendid.
  • z ja w saavad olla kas positsionaalsed või vaikimisiväärtustega argumendid, kusjuures vaikimisiväärtustega argumendid peavad järgnema positsionaalsetele.
  • a ja b on vaikimisiväärtustega argumendid.
In [44]:
def f(x, y, /, z, w, *, a, b):  # Erinevat tüüpi argumendid on eristatud kasutades / ja *.
    print(x, y, z, w, a, b)

f(1, 2, z=3, w=4, a=5, b=6)  # Erinevad tüübid: järjekord oluline.
f(1, 2,   3, w=4, a=5, b=6)
f(1, 2,   3,   4, b=5, a=66)  # Nimega argumentide puhul pole järjekord on oluline.
1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 66 5

3.1.4  Etteteadmata arv võtmesõnaga ehk vaikeväärtustega argumente, **kwargs¶

Operaator ** pakib argumendid milleks on võtmesõnade ja väärtuste paarid sõnastikku.

In [45]:
def test(**kwargs):  # Meelevaldne arv argumente.
    print(type(kwargs))  # Operaator ** pakis argumendid sõnaraamatusse.
    print(kwargs)

test(a=77, b=66, c1=77, c2=88, c3=12)
<class 'dict'>
{'a': 77, 'b': 66, 'c1': 77, 'c2': 88, 'c3': 12}
In [46]:
test()  # NB! Argumente saad ka mitte sisestada.
<class 'dict'>
{}
In [47]:
def myFunc(**kwargs):
    for key, value in kwargs.items():  # **kwargs argument on sõnastik.
        print(f"{key} --> {value}")

myFunc(key1='Python', key2='Snake')
key1 --> Python
key2 --> Snake

Nii nagu operaatoriga * saame ka siin kasutada operaatorit ** funktsiooni väljakutses.

In [48]:
def myFunc(**kwargs):
    for key, value in kwargs.items():
        print(f"{key} --> {value}")
        
items = {'key1':'Python', 'key2':'snake'}
myFunc(**items)  # Pakin lahti, et funk. saaks kokku pakkida.
key1 --> Python
key2 --> snake
In [49]:
def myFunc(k1='x', k2='y'):
    print(f"k1 --> {k1}")
    print(f"k1 --> {k2}")
        
items = {'k1':'Python', 'k2':'snake'}
myFunc(**items)  # Pakin lahti.
k1 --> Python
k1 --> snake

3.1.5  Argumentide segakasutus¶

Järjekord on oluline. Funktsiooni argumendi sulgudes kasutame erinevat tüüpi argumente järgmises järjekorras:

  1. Tavalised positsionaalsed argumendid.
  2. Vaikimisiväärtustega argumendid (nende väärtusi saab alati üle kirjutada positsionaalselt).
  3. Meelevaldne arv positsionaalsed argumendid, *args-tüüpi argumendid.
  4. Vaikimisiväärtustega argumendid (nende väärtusi saab alati üle kirjutada viidates neile nimepidi).
  5. Meelevaldne arv mittepositsionaalsed võtmesõnaga argumendid, **kwargs-tüüpi argumendid.

Näiteid: Lubatud järjekorrad.

In [50]:
def test(x, y, *args, z=3, **kwargs):
    print(x)
    print(y)
    print(args)
    print(z)
    print(kwargs),
    
test(1, 2, 33, 44, z=33, a=444, b=555)  # Argument z ülekirjutamine. Argumendile z peab viitama nimepidi.
print()
test(1, 2, 33, 44, 33, a=444, b=555)  # Argument z jäeti rahule.
1
2
(33, 44)
33
{'a': 444, 'b': 555}

1
2
(33, 44, 33)
3
{'a': 444, 'b': 555}

Saab vajadusel ka nii. Sellist süntaksit kasutatakse keerukates funktsioonides. Meelevaldse arvuga argumenditüübid on viimased.

In [51]:
def test(x, y, z=3, *args, **kwargs):  # Vaikimisiväärtustega argument z pole kohustuslik.
    print(x)
    print(y)
    print(z)  # Käitub rohkem nagu positsionaalne argument.
    print(args)
    print(kwargs)
    
test(1, 2, 33, 44, 44, a=444, b=555)  # Argument z positsionaalne ülekirjutamine. Argumendi z väärtust pole võimalik mitte sisestada.
1
2
33
(44, 44)
{'a': 444, 'b': 555}
In [52]:
def test(x, y, z1=2, *args, z2=3, **kwargs):
    print(x)
    print(y)
    print(z1)
    print(args)
    print(z2)
    print(kwargs),
    
test(1, 2, 33, 44, z2=33, a=444, b=555)  # Argument z1 positsionaalne ülekirjutamine. Argumendile z2 peab viitama nimepidi.
print()
test(1, 2, 33, 44, 33, a=444, b=555)  # Argument z2 jäeti rahule.
1
2
33
(44,)
33
{'a': 444, 'b': 555}

1
2
33
(44, 33)
3
{'a': 444, 'b': 555}

Anonüümne funktsioon toetab kõiki argumenditüüpe.

In [53]:
f = lambda x, y=2, *args, **kwargs: x*(sum(args))  # Algoritm ei kasuta argumenti y ja **kwargs argumente.
f(2, 2, 1, 1)  # **kwargs argumente ei kasutanudki. Argument y jäi samaks.
Out[53]:
4

4  Muutujate skoop¶

4.1  LEGB reegel¶

Skoop tähistab programmis mingit kehtivuspiirkonda (või mõjupiirkonda), milles on muutujad seotud kindlate väärtustega. Pythonis eristatakse nelja skoopi mis on loetletud Joonisel 1. Pythonis kehtib nn. LEGB reegel. See reegel kirjeldab millises järjekorras ja millisest skoobist (nimeruumist) otsitakse lähtekoodis kasutatud muutujate väärtusi, vt. Joonis 1 ja 2.

No description has been provided for this image
Joonis 1: Akronüümi LEGB tähendus ja nn. LEGB reegel visualiseeritult.
No description has been provided for this image
Joonis 2: LEGB reegel visualiseeritult.

4.2  Lokaliseeriv ja muud skoobid¶

Pythonis on ainult üks koodiblokk mis loob lokaliseeriva ja/või kapseldava skoobi, selleks on def blokk. Lokaalne skoop kaitseb enda muutujaid varjates neid muude skoopide eest. Erinevate skoopide tähendus selgub uurides allolevaid näiteid.

4.2.1  Lokaalne ja globaalne skoop¶

Pythonis on funktsiooni sees loodud muutujad lokaalsed, need on nähtavad ainult funktsiooni seest. Asudes funktsiooni blokkis, alustab Pythoni interpretaator muutujate väärtuste otsimist lokaalsest skoobist, siis kapseldatud või pesastatud skoobist, siis globaalsest skoobist ja lõpuks sisseehitatud objektide nimeruumi skoobist, vt. Joonis 1 ja 2.

In [54]:
x = 'Globaalne x'  # Globaalne skoop.

def test():
    y = 'Lokaalne y'  # Bloki seest vaadates on see lokaalne skoop.
    print(y)

test()
print(x)
print(y)  # Ei näe funktsioonibloki sisse, funktsioon kaitseb enda muutujaid.
Lokaalne y
Globaalne x
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[54], line 9
      7 test()
      8 print(x)
----> 9 print(y)

NameError: name 'y' is not defined
In [55]:
x = 'Globaalne x'

def test():
    y = 'Lokaalne y'
    print(y, '<- Funktsiooni sees.')
    print(x, '<- Funktsiooni sees.')  # LEGB järjekord: L -> K(puudub) -> G.

test()
print(x)
Lokaalne y <- Funktsiooni sees.
Globaalne x <- Funktsiooni sees.
Globaalne x
In [56]:
x = 'Globaalne x'

def test():
    x = 'Lokaalne x'  # Ei kirjuta globaalset üle.
    print(x)

test()
print(x)  # Funktsioon test ei kirjutanud x-i üle.
Lokaalne x
Globaalne x

4.2.2  Lausend global¶

Kui tahame funksiooni seest globaalset muutujat üle kirjutada ehk annda funktsiooni lokaalsele skoobil juurdepääsu globaalsele, muudame lokaalse muutuja globaalseks kasutades lausendit global.

In [57]:
x = 'Globaalne x'

def test():
    global x 
    x = 'Lokaalne x'  # Kirjutab globaalse muutuja üle.
    print(x)

test()
print(x)
Lokaalne x
Lokaalne x

Või kui soovime defineerida uut globaalset muutuja lokaalses skoobis ehk def bloki sees.

In [58]:
def test():
    global x  # Globaalne muutuja.
    x = 'Lokaalselt loodud x'  # Defineerime muutuja ja omistame väärtuse.
    print(x)

test()
print(x)  # Näeb lokaalses skoobis loodud globaalset muutujat x.
Lokaalselt loodud x
Lokaalselt loodud x
In [59]:
def test(z):  # Argument z on lokaalse skoobi muutuja.
    print(z)

test('Lokaalne z')
print(z)  # Ei näe lokaalsesse skoopi.
Lokaalne z
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[59], line 5
      2     print(z)
      4 test('Lokaalne z')
----> 5 print(z)

NameError: name 'z' is not defined

4.2.3  Kapseldav ja kapseldatud ehk pesastatud skoop¶

Pythonis on kapseldav skoop defineeritud def blokiga. Muutujatele väärtuste otsimine käib endiselt LEGB reegli järgi, kusjuures kapseldavaid skoope saab olla rohkem kui üks, vt. Joonis 1 ja 2, vrd. Joonis 3.

No description has been provided for this image
Joonis 3: LEGB reegel juhul kui esineb kaks lokaalset skoopi. Kusjuures vasakpoolne lokaalne skoop on rohkem kui üks kord kapseldatud ja parempoolne pole kapseldatud.

Uurime näidet kus lokaalne skoop, mis on defineeritud funktsiooniga sise, on kapseldatud kaks korda.

In [60]:
x = "globaalne x"

def valisem():
    def valine():
        def sise():
            print('sise --->', x)  # L -> E -> E -> G
        sise()
        print('valine -->', x)  # L -> E -> G
    valine()
    print('valisem -->', x)  # L -> G

valisem()
sise ---> globaalne x
valine --> globaalne x
valisem --> globaalne x

Lokaalne skoop, mis on defineeritud funktsiooniga sise, on kapseldatud üks korda.

In [61]:
def valine():      # Kapseldav skoop, kapseldab ühte lokaalset skoopi.
    x = 'Kapseldav x'
    def sise():    # Lokaalne skoop.
        x = 'Lokaalne x'
        print(x)
    sise()
    print(x)

valine()
Lokaalne x
Kapseldav x
In [62]:
def valine():
    x = 'Kapseldav x'
    def sise():
        print(x)  # LEGB järjekord: L -> K.
    sise()
    print(x)

valine()
Kapseldav x
Kapseldav x
In [63]:
def valine():
    def sise():
        z = 'Lokaalne z'
        print(z)  # LEGB järjekord: L.
    sise()
    print(z)  # LEGB: Ei näe kapseldatud funktsiooni skoopi.

valine()
Lokaalne z
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[63], line 8
      5     sise()
      6     print(z)  # LEGB: Ei näe kapseldatud funktsiooni skoopi.
----> 8 valine()

Cell In[63], line 6, in valine()
      4     print(z)  # LEGB järjekord: L.
      5 sise()
----> 6 print(z)

NameError: name 'z' is not defined

4.2.4  Lausend nonlocal¶

Kui soovime lokaalses skoobis juurdepääsu kapseldava skoobi muutujale kasutame lausendit nonlocal

In [64]:
def valine():
    x = 'Kapseldav x'
    def sise():
        nonlocal x   # Kapseldava skoobi muutuja.
        x = 'Lokaalne x'  # Kirjutab kapseldava muutuja üle.
        print(x)
    sise()
    print(x)

valine()
Lokaalne x
Lokaalne x

Kaks astet kapseldavaid skoope. Pesastatud funktsioonid.

In [65]:
def valisem():
    x = 'Kapseldav kapseldav x'
    def valine():
        def sise():
            nonlocal x  # Kapseldava skoobi muutuja.
            x = 'Lokaalne x'
            print('sise --->', x)
        nonlocal x  # Kapseldava kapseldava skoobi muutuja.
        x = 'Kapseldav x'
        sise()
        print('valine -->', x)
    valine()
    print('valisem -->', x)

valisem()
sise ---> Lokaalne x
valine --> Lokaalne x
valisem --> Lokaalne x

Märkus 1: Lausendite global ja nonlocal kasutamine pole soovituslik. Kui sa kipud neid tihti kasutama siis suure tõenäosusega teed midagi valesti.
Märkus 2: Globaalseid muutujaid ei ole soovituslik kasutada funktsiooni see. Kasuta funktsiooni argumente.

4.2.5  Sisseehitatud objektide nimeruumi skoop (Built-ins scope)¶

Sisseehitatud objektide nimeruumi skoop on spetsiaalne Pythoni skoop, mis luuakse või laaditakse alati, kui käivitate programmi. See skoop sisaldab Pythoni sisseehitatud muutujate ja objektide nimesid. Nimed selles skoobis on nähtavad kõikidest skoopidest. Näiteks funktsioon print on Pythoni sisseehitatud standardteegi funktsioon ja see kuulub sisseehitatud objektide nimeruum skoopi.

Nimeruumi nimede arv ja sisu järgivaatamine:

In [66]:
import builtins  # Lausendist import räägime tulevastes loengutes.

print(len(dir(builtins)))
#dir(builtins)    # Vaata iseseisvalt mida nimeruum sisaldab.
160

Ettevaatust: Kuna Pythonis on võimalik sisseehitatud objektide nimeruumi nimesid üle kirjutada siis...

In [67]:
min([1, 2])  # Funktsioon min on built-ins skoobis.
Out[67]:
1
In [68]:
def min(*args):  # Funktsioon min on globaalses skoobis.
    print('Juulius Tipikas.')

min([1, 2])  # LEGB reegel: Leib funktsiooni min globaalsest skoobist enne built-ins skoopi.
Juulius Tipikas.

Me oleme selle ohuga eelnevalt juba tutvunud. Nüüd saime detailsemalt teame miks sisseehitatud objektide nimede kasutamine on halb mõte (LEGB reegel).

4.2.6  Mitu lokaalset skoopi¶

Üks lokaalne skoop ei saa leida või kasutada teise lokaalse skoobi muutujaid. Lokaalsed skoobid pole üksteisele nähtavad, vt. Joonis 3.

In [69]:
def skoop1():
    muutuja = 'Python'
    print(muutuja)

def skoop2():
    muutuja = 'Juulius'  # Ei kirjutata üle.
 
skoop2()
skoop1()
Python
In [70]:
def skoop1():
    muutuja = 'Python'
    skoop2()
    print(muutuja)

def skoop2():
    muutuja = 'Juulius'  # Ei kirjutata üle.

skoop1()
Python

4.2.7  Muutuja skoop ja tingimuslaused¶

Tingimuslausega if-blokis loodud muutujad on globaalses skoobis. See on üldine reegel millega kaasneb järgmine nüanss.
NB! Kui if-bloki tingimus mille all muutuja on defineeritud ei teostu siis ei ole see muutuja globaalsest skoobist leitavad.

In [71]:
if True:
    visible = 'Python'
else:
    invisible = 'Juulius'

print(visible)
print(invisible)
Python
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[71], line 7
      4     invisible = 'Juulius'
      6 print(visible)
----> 7 print(invisible)

NameError: name 'invisible' is not defined

5  Itereerimine ja jadade filtreerimine¶

5.1 for-in avaldised¶

for-in avaldis on iteraatorite, generaatorite ja iteraatortoega objektide defineerimise võte ja filter kujul:

[<avaldis või algoritm> for <element> in <iteraatortoega obj.>]

Avaldise abil loome jadasid ja filtreeritud jadasid kasutades ainult ühte rida koodi. Sulgude valikuga kontrollime kas defineerima listi, hulga või sõnastiku. Joonis 4 kujutab listi loomise ja filtreerimise voodiagrammi.

No description has been provided for this image
Joonis 4: for-in avaldise voodiagramm. Kriipsjoonega tähistatud osad on for tsükli automatiseeritud tegevused.

5.1.1  Listi avaldis (list comprehension)¶

for-in avaldis abil saame defineerida liste. Alustame meile teadaolevate võtetega ja siis uurime neile vastavaid üherealisi avaldisi.

In [72]:
ruudud = []  # Tühi list.

for i in range(5):
    ruudud.append(i ** 2)  # Meetod append on listi meetod.

ruudud
Out[72]:
[0, 1, 4, 9, 16]

Nüüd kasutades for-in avaldist:

In [73]:
[i ** 2 for i in range(5)]
Out[73]:
[0, 1, 4, 9, 16]
In [74]:
lst = [i for i in range(5)]
print(lst)

ruudud = [i ** 2 for i in lst]  # Loend lst on iteraatortoega objekt.
print(ruudud)
[0, 1, 2, 3, 4]
[0, 1, 4, 9, 16]

5.1.2  Hulga avaldis (set comprehension)¶

Sarnaselt listi loomisele kasutades for-in avaldisele saame defineerida ka hulkasid.

In [75]:
{i for i in range(4)}  # NB! Loogelised sulud.
Out[75]:
{0, 1, 2, 3}
In [76]:
{i for i in [1, 2, 3, 4, 4]}  # Ainult unikaalsed listi elemendid.
Out[76]:
{1, 2, 3, 4}
In [77]:
ruudud = {i ** 2 for i in range(4)}
ruudud
Out[77]:
{0, 1, 4, 9}
In [78]:
{i for i in 'Python'}  # Hulk pole järjestatud.
Out[78]:
{'P', 'h', 'n', 'o', 't', 'y'}

5.1.3  Sõnastiku avaldis (dictionary comprehension)¶

Väikese lisatööga saame ka sõnastikke defineerida.

In [79]:
{i: i ** 2 for i in range(3)}
Out[79]:
{0: 0, 1: 1, 2: 4}
In [80]:
ruudud = {f'Key{i}': i ** 2 for i in range(3)}

print(type(ruudud))
print(ruudud)
<class 'dict'>
{'Key0': 0, 'Key1': 1, 'Key2': 4}

NB! Korteeži avaldist pole olemas kuna see defineerib objekti mida selles loengus ei käsitleta (Järgmise nädala loengus räägime generaatoritest ja nende abil loodud iteraatoritest).

5.2.  Pesastatud for-in avaldised (nested comprehensions)¶

Nii nagu saab pesastada tavalisi for tsükli koodiblokke saame seda teha ka avaldisega.

In [81]:
lst = []
for x in [1, 2, 3]:  # Pesastav tsükkel.
    for y in [5, 5]:  # Pesastatud tsükkel.
        lst.append(x * y)

lst
Out[81]:
[5, 5, 10, 10, 15, 15]

Nüüd kasutades for-in avaldist:

In [82]:
[x * y for x in [1, 2, 3] for y in [5, 5]]
Out[82]:
[5, 5, 10, 10, 15, 15]

Kaks pesastamist:

In [83]:
lst = []
for x in [1, 2, 3]:  # Pesastav tsükkel.
    for y in [5, 5]:  # Pesastatud tsükkel 1.
        for z in [1, 1]:  # Pesastatud tsükkel 2.
            lst.append(x * y * z)

lst
Out[83]:
[5, 5, 5, 5, 10, 10, 10, 10, 15, 15, 15, 15]
In [84]:
[x * y * z for x in [1, 2, 3] for y in [5, 5] for z in [1, 1]]
Out[84]:
[5, 5, 5, 5, 10, 10, 10, 10, 15, 15, 15, 15]

5.3  for-in-if avaldised (conditional comprehensions)¶

5.3.1  Filtreeritud iteraatortoega objektid ja iteraatorid¶

Teatud jada liikmete välja filtreerimiseks või muutmiseks lisame eelnevate avaldiste lõppu if klausli. Filtreeritud listi süntaks on järgmine:

uus_list = [<avaldis> for <element> in <iteraatortoega obj.> if <tingimus>]

Sulgude valikuga saame kontrollida kas loome liste, hulkasid või sõnastikke. Joonis 5 kujutab eelmainitud filtreerimisprotsessi voodiagrammi.

No description has been provided for this image
Joonis 5: for-in-if avaldise voodiagramm. Kriipsjoonega tähistatud osad on for tsükli automatiseeritud tegevused.

Nii nagu eespool, alustame meile teadaolevate võtetega ning siis võrdleme neile vastavaid üherealisi avaldisi.

In [85]:
num_less_ten = []
num = [1, 2, 55, 3]

for i in num:
    if i < 10:
        num_less_ten.append(i)
        
num_less_ten
Out[85]:
[1, 2, 3]
In [86]:
vowels = []
lause = 'Juulius'

for i in lause:
    if i in 'aeiouõäöü':
        vowels.append(i)

vowels
Out[86]:
['u', 'u', 'i', 'u']

Ekvivalentsed kood kasutades for-in-if avaldist.

In [87]:
num_less_ten = [i for i in num if i < 10]
num_less_ten
Out[87]:
[1, 2, 3]
In [88]:
vowels = [i for i in lause if i in 'aeiouõäöü']
vowels
Out[88]:
['u', 'u', 'i', 'u']

Muid näiteid:

In [89]:
[i for i in range(10) if i % 3 == 0]  # Kolmega jaguvad arvud.
Out[89]:
[0, 3, 6, 9]
In [90]:
numbers = [1, 2, 2, 55, 3, 4]
set_less_ten = {i for i in numbers if i < 10}  # Tulemus: Filtreeritud hulk.
set_less_ten  # Hulk.
Out[90]:
{1, 2, 3, 4}
In [91]:
numb = [1, 2, 55, 3, 4]
{i: i for i in numb if i < 10}  # Tulemus: filtreeritud sõnastik.
Out[91]:
{1: 1, 2: 2, 3: 3, 4: 4}

5.4  Muud segaavaldised¶

Kasutatakse jadade loomiseks ja iteraatortoega objektide ning iteraatorite filtreerimiseks.

5.4.1  if-else valikuavaldis koos for-in avaldisega¶

No description has been provided for this image
Joonis 6: if-else-for-in avaldise voodiagramm. Kriipsjoonega tähistatud osad on for tsükli automatiseeritud tegevused.

Kokku saame if-else-for-in avaldise. Sulgude valikuga saame luua kas liste, hulkasid või sõnastikke. Joonis 6 kujutab avaldise voodiagrammi. Eespool tutvustatud for-in avaldises saab, näiteks, kasutada Loengus 4 tutvustatud if-else valikuavaldist. Tavaline for-in avaldis on kujul:

[<avaldis> for <element> in <iteraator, iteraatortoega obj.>]

Asenda eelmisel real olev <avaldis> if-else valikuavaldisega:

[<avaldis 1> if <tingimus> else <avaldis 2> for <element> in <iteraator, iteraatortoega obj.>]

See avaldis on ekvivalentne järgmise for tsükliga kujul:

tulemus = []
for <element> in <iteraator, iteraatortoega obj.>:
    if <tingimus>:
        tulemus.append(<avaldis 1>)
    else:
        tulemus.append(<avaldis 2>)  

Sarnaselt saab luua hulga ja sõnaraamatute segaavaldisi mis väljastavad tulemused vastavalt kas hulka või sõnaraamatusse.

Näide: Filtreeri loend a: Juhul kui loendi arv on suurem või võrdne $50$-ga liida sellele $1$, juhul kui arv on väiksem kui $50$ liida sellele $5$.

In [92]:
a = [22, 45, 50, 98, 69]
[x + 1 if x >= 50 else x + 5 for x in a]
Out[92]:
[27, 50, 51, 99, 70]

Ekvivalentne kuju kasutades for tsüklit:

In [93]:
b = []
for x in a:
    if x >= 50:
        b.append(x + 1)
    else:
        b.append(x + 5)
        
print(b)
[27, 50, 51, 99, 70]

5.4.2  if-else valikuavaldis koos for-in-if avaldisega¶

No description has been provided for this image
Joonis 7: if-else-for-in-if avaldise voodiagramm. Kriipsjoonega tähistatud osad on for tsükli automatiseeritud tegevused.

Kokku saame if-else-for-in-if avaldise. Sulgude valikuga saame luua kas liste, hulkasid või sõnastikke. Joonis 7 kujutab avaldise voodiagrammi. Eespool tutvustatud for-in-if avaldises saab kasutada Loengus 4 tutvustatud if-else valikuavaldist. Tavaline for-in-if avaldis on kujul:

[<avaldis> for <element> in <iteraator, iteraatortoega obj.> if <tingimus>]

Asenda eelmisel real olev <avaldis> if-else valikuavaldisega:

[<avaldis 1> if <tingimus 2> else <avaldis 2> for <element> in <iteraator, iteraatortoega obj.> if <tingimus 1>]

See avaldis on ekvivalentne järgmise for tsükliga kujul:

tulemus = []
for <element> in <iteraator, iteraatortoega obj.>:
    if <tingimus 1>:
        if <tingimus 2>:
            tulemus.append(<avaldis 1>)
        else:
            tulemus.append(<avaldis 2>)

Sarnaselt saab luua hulga ja sõnaraamatute segaavaldisi mis väljastavad tulemused vastavalt kas hulka või sõnaraamatusse.

Näide: Filtreeri loend a: 1) Elimineeri paaritud arvud. 2) Juhul kui leitud paaris arv on suurem või võrdne $50$-ga liida sellele $1$, juhul kui paaris arv on väiksem kui $50$ liida sellele $5$.

In [94]:
a = [22, 13, 45, 50, 98, 69]
[x + 1 if x >= 50 else x + 5 for x in a if x % 2 == 0]
Out[94]:
[27, 51, 99]

Ekvivalentne kuju kasutades for tsüklit:

In [95]:
b = []
for x in a:
    if x % 2 == 0:
        if x >= 50:
            b.append(x + 1)
        else:
            b.append(x + 5)

print(b)
[27, 51, 99]

















☻   ☻   ☻