Loeng 8: Sisseehitatud funktsioonide ülevaade, funktsiooni sulund, dekoraator ja @ operaator, lausend assert, moodul ja faililaiend .py, programmi käivitamine käsurealt konsoolis, integreeritud arenduskeskkonna tutvustus ja kasutamine, kursusetöö juhend ja näidis¶

Viimati uuendatud 10.12.2024.

Koodinäited¶

Sisseehitatud funktsioonide ülevaade (Built-in functions)¶

Siiamaani oleme tutvunud muuseas sellega kuidas defineerida ja kasutada funktsioone, protseduure ja generaatoreid. Me oleme selleks kasutanud erinevaid tüüpe argumente (positsionaalsed, vaikeväärtusega, args- ja kwargs-tüüpi argumendid), lausendeid def ja lambda, ning lausendeid return ja yield, jne.

Nagu me juba teame tuleb Pythoniga kaasa hulk sisseehitatud funktsioone, järgnev on lühike ülevaade olulisematest. Osad nenedest funktsioonidest on meile juba tuttavad.

Valik sisseehitatud funktsioone:

range(stop)
range(start, stop[, step])
input([prompt])
len(s)
max(iterable[, key])
max(arg1, arg2, *args[, key])
min ...
id(object)
hash(object)
int(x, base=10)
float([x])
complex([real[, imag]])
str(object='')
list([iterable])
tuple([iterable])
dict(**kwarg)
dict(mapping, **kwarg)
dict(iterable, **kwarg)
set([iterable])
frozenset([iterable])
bool([x])
divmod(a, b)
pow(x, y[, z])
abs(x)
round(number[, ndigits])
chr(i)
bin(x)
ord(c)
hex(x)
all(iterable)
any(iterable)
reversed(seq)
sorted(iterable[, key[, reverse]]])
map(func, iterable, ...)
filter(func, iterable)
sum(iterable[, start])
zip([iterable, ...])
enumerate(sequence[, start=0])}
dir([object])
locals()
globals()
eval(expression[, globals[, locals]])
exec(source[, globals[, locals[, closure]]])
repr(object)
isinstance(object, classinfo)
issubclass(class, classinfo)
callable(object)
iter(object[, sentinel])

Kõigi sisseehitatud funktsioonide järgivaatamiseks kasuta funktsiooni dir:

In [1]:
import builtins  # Lausendist importi räägime tulevastes loengutes.
#dir(builtins)  # Vt. iseseisvalt

Sisseehitatud funktsioonide kasutamise näited:¶

Funktsioon round¶

Kasutame arvude ümardamiseks. Funktsiooni kasutamise süntaks on järgmine:

round(<number>[, <ndigits>])

Sama info koos funktsiooni DocStringiga ehk abiinfoga:

In [2]:
round?
Signature: round(number, ndigits=None)
Docstring:
Round a number to a given precision in decimal digits.

The return value is an integer if ndigits is omitted or None.  Otherwise
the return value has the same type as the number.  ndigits may be negative.
Type:      builtin_function_or_method

NB! Viiega lõppevaid murdarve ümardame lähima paarisarvu suunas või lähima küsitud paarisarvulise komakohani, mitte suurema arvu suunas nagu õpetati enamustes põhikoolides:

In [3]:
round(4.5)  # Tutvu iseseisvalt ja vt alla: Nn. "odd-even rounding rule".
Out[3]:
4
In [4]:
print(round(0.125, 2))  # Ümarda kahe komakohani kasutades odd-even rounding rule'i.
print(round(0.005, 2))  # Ümarda kahe komakohani.
0.12
0.01

Uurides ülemise raku rida 2 tundub, et Python rikub enda reegleid mille kohaselt $0.005 \approx 0.00 \equiv 0$ mitte $0.005 \approx 0.01$ nagu just juhtus. Kuidas seda seletada? See juhtus kuna ujuvkomakohaga arvu ei saa arvutis üldjuhul täpselt esitada. Loe lisaks: https://docs.python.org/3.13/library/functions.html#round

In [5]:
round(2.87678, 3)  # Ümarda kolmanda komakohani.
Out[5]:
2.877

Pythoni reegli kus me ümardame lähime paarisarvuni (odd-even rounding rule) kasutamise põhjendus:

In [6]:
print('Arv  -->  Ümardatud arv')
lst0, lst = [], []
for i in range(10):
    arv = i + 0.5
    round_arv = round(arv)  # Pythoni funktsioon round.
    print(arv, ' --> ', round_arv)
    lst0.append(arv)
    lst.append(round_arv)

print(f'Algandmete keskmine: {sum(lst0) / 10}')
print('Ümardatud arvude keskmine:', sum(lst) / 10)
Arv  -->  Ümardatud arv
0.5  -->  0
1.5  -->  2
2.5  -->  2
3.5  -->  4
4.5  -->  4
5.5  -->  6
6.5  -->  6
7.5  -->  8
8.5  -->  8
9.5  -->  10
Algandmete keskmine: 5.0
Ümardatud arvude keskmine: 5.0

Võrdluseks: Kui kasutada põhikoolis õpetatud valet reeglit kus ümardatakse alati ülesse kui pole muid juhendeid antud, siis saame:

In [7]:
import math  # Lausendist importi räägime tulevastes loengutes.

print('Arv  -->  Ümardatud arv')
lst0, lst = [], []
for i in range(10):
    arv = i + 0.5
    round_arv = math.ceil(arv)  # Ümardame ülesse.
    print(arv, ' --> ', round_arv)
    lst0.append(arv)
    lst.append(round_arv)

print(f'Algandmete keskmine: {sum(lst0) / 10}')
print('Ümardatud arvude keskmine:', sum(lst) / 10)
Arv  -->  Ümardatud arv
0.5  -->  1
1.5  -->  2
2.5  -->  3
3.5  -->  4
4.5  -->  5
5.5  -->  6
6.5  -->  7
7.5  -->  8
8.5  -->  9
9.5  -->  10
Algandmete keskmine: 5.0
Ümardatud arvude keskmine: 5.5

Funktsioon sorted¶

Iteraatortoega objektide sorteerime. Funktsiooni kasutamise süntaks on järgmine:

sorted(<iterable>, /, *[, key [, reverse]])

Sama info koos funktsiooni DocStringiga ehk abiinfoga:

In [8]:
sorted?
Signature: sorted(iterable, /, *, key=None, reverse=False)
Docstring:
Return a new list containing all items from the iterable in ascending order.

A custom key function can be supplied to customize the sort order, and the
reverse flag can be set to request the result in descending order.
Type:      builtin_function_or_method
In [9]:
L = ['cccc4','b3','dd2','aaa1']  # Lisaks numberitle järjestame ka tähestikulises järjekorras.
sorted(L)
Out[9]:
['aaa1', 'b3', 'cccc4', 'dd2']
In [10]:
it = iter([3, 2, 1])
sorted(it)  # Iteraator on iteaatortoega objekt.
Out[10]:
[1, 2, 3]
In [11]:
sorted(L, key=len)  # Võtmesõna key, sotrteerimisel kasuta funktiooni len.
Out[11]:
['b3', 'dd2', 'aaa1', 'cccc4']
In [12]:
sorted(L, key=len, reverse=True)
Out[12]:
['cccc4', 'aaa1', 'dd2', 'b3']
In [13]:
sorted(L, key=lambda x: float(x[-1]))  # Enda loodud sorteerimisefunktsiooni kasutus.
Out[13]:
['aaa1', 'dd2', 'b3', 'cccc4']

Sarnane listi meetodiga sort mis omad samu argumente. DocStringis tähendab IN-PLACE, et algne mälus olnud list muudetakse sorteeritud listiks või teisisõnu asendatakse sorteeritud listiga. Eelmainitud funktsioon sorted loob uue iteraatortoega objekti.

In [14]:
L.sort?
Signature: L.sort(*, key=None, reverse=False)
Docstring:
Sort the list in ascending order and return None.

The sort is in-place (i.e. the list itself is modified) and stable (i.e. the
order of two equal elements is maintained).

If a key function is given, apply it once to each list item and sort them,
ascending or descending, according to their function values.

The reverse flag can be set to sort in descending order.
Type:      builtin_function_or_method
In [15]:
L.sort(key=len, reverse=True)
L
Out[15]:
['cccc4', 'aaa1', 'dd2', 'b3']

Funktsioon map¶

Funktsioon map rakendab meelevaldselt valitud funktsiooni iga iteraatortoega objekti elemendile ja väljastab tulemused iteraatorisse. Meeldetuletus: Üle iteraatori saab itererida, iteraator mäletab enda viimast väljakutset ning sellele saab rakendada funktsiooni next järgmiste väljakutsete genereerimiseks, vt. Loeng 7. Funktsiooni kasutamise süntaks on järgmine:

map(<func>, *<iterables>)

Sama info koos funktsiooni DocStringiga ehk abiinfoga:

In [16]:
map?
Init signature: map(self, /, *args, **kwargs)
Docstring:     
map(func, *iterables) --> map object

Make an iterator that computes the function using arguments from
each of the iterables.  Stops when the shortest iterable is exhausted.
Type:           type
Subclasses:     
In [17]:
a = map(len, ('abc', 'cd', 'efgh'))

print(next(a))  # Esimene iteraat.
print(list(a))  # Funktsioon list itereerib üle iteraatori ammendumiseni.
3
[2, 4]

Kahe argumendiga funktsioonid:

In [18]:
a = map(pow, [2, 2, 2], [1, 2, 3])  # Funktsioon pow eeldab kahte argumenti.

list(a)
Out[18]:
[2, 4, 8]
In [19]:
b = map(lambda x, y: x ** y, [2, 2, 2], [1, 2, 3])

list(b)
Out[19]:
[2, 4, 8]
In [20]:
c = map(lambda x, y: x ** y, [2, 2, 2], (1, 2, 3))

for i in c:  # Itereerin üle iteraatori c.
    print(i)
2
4
8

Funktsioon filter¶

Funktsioon filter tagastab iteraatortoega jada elemendid, mille korral meelevaldselt valitud funktsioon(element) == True. Väljastatud tulemuse andmetüüp on iteraator. Funktsiooni kasutamise süntaks on järgmine:

filter(<function> or <None>, <iterable>)

Sama info koos funktsiooni DocStringiga ehk abiinfoga:

In [21]:
filter?
Init signature: filter(self, /, *args, **kwargs)
Docstring:     
filter(function or None, iterable) --> filter object

Return an iterator yielding those items of iterable for which function(item)
is true. If function is None, return the items that are true.
Type:           type
Subclasses:     
In [22]:
iteraator = filter(bool, [True, 1, (1,), False, [], 0])

print(list(iteraator))
[True, 1, (1,)]
In [23]:
def positive(x):
    if x > 0:
        return True
    else:
        return False

a = filter(positive, [-1, 0, 1, 2])
print(list(a))
[1, 2]
In [24]:
def f(x):
    if x > 0:
        return x  # Positiivne arv = mittetriviaalne täisarv.
 
print(list(filter(f, [-1, 0, 1, 2])))  # Mittetriviaalse objekt tõeväärtus on True.
[1, 2]

Anonüümne funktsioon ja funktsioon filter:

In [25]:
a = filter(lambda x: x % 2 == 0, range(11))
list(a)
Out[25]:
[0, 2, 4, 6, 8, 10]
In [26]:
a = filter(bool, 'Juulius on maskott.')

for i in a:
    print(i, end=' ')
J u u l i u s   o n   m a s k o t t . 
In [27]:
print(list(filter(None, 'Juulius')), end=' ')
['J', 'u', 'u', 'l', 'i', 'u', 's'] 

Funktsioon zip¶

Funktsioon zip seob iteraatortoega jadade elemendid elementhaaval korteeži. Väljastatud tulemus edastatakse iteraatorisse. Funktsiooni kasutamise süntaks on järgmine:

zip(*<iterables>)

Sama info koos funktsiooni DocStringiga ehk abiinfoga:

In [28]:
zip?
Init signature: zip(self, /, *args, **kwargs)
Docstring:     
zip(*iterables, strict=False) --> Yield tuples until an input is exhausted.

   >>> list(zip('abcdefg', range(3), range(4)))
   [('a', 0, 0), ('b', 1, 1), ('c', 2, 2)]

The zip object yields n-length tuples, where n is the number of iterables
passed as positional arguments to zip().  The i-th element in every tuple
comes from the i-th iterable argument to zip().  This continues until the
shortest argument is exhausted.

If strict is true and one of the arguments is exhausted before the others,
raise a ValueError.
Type:           type
Subclasses:     
In [29]:
a = zip('abc', 'ABC')
print(list(a))
[('a', 'A'), ('b', 'B'), ('c', 'C')]

Maatriksi transponeerimine:

In [30]:
M = [(1, 2), (3, 4)]  # Tõlgenda 2x2 maatriksina.
MT = zip(*M)          # Transponeeritud M.
print(list(MT))              
[(1, 3), (2, 4)]

Funktsioon enumerate¶

Funktsioon enumerate nummerdab iteraatortoega jada elemendid ja väljastab tulemuse korteeži. Väljastatud tulemuse andmetüüp on iteraator. Funktsiooni kasutamise süntaks on järgmine:

enumerate(<iterable> [, start])

Sama info koos funktsiooni DocStringiga ehk abiinfoga:

In [31]:
enumerate?
Init signature: enumerate(iterable, start=0)
Docstring:     
Return an enumerate object.

  iterable
    an object supporting iteration

The enumerate object yields pairs containing a count (from start, which
defaults to zero) and a value yielded by the iterable argument.

enumerate is useful for obtaining an indexed list:
    (0, seq[0]), (1, seq[1]), (2, seq[2]), ...
Type:           type
Subclasses:     
In [32]:
a = enumerate(['a', 'b', 'c'])
list(a)
Out[32]:
[(0, 'a'), (1, 'b'), (2, 'c')]
In [33]:
a = enumerate(['a', 'b', 'c'], start=2)
list(a)
Out[33]:
[(2, 'a'), (3, 'b'), (4, 'c')]

Kasutame sageli itereerimisega koos:

In [34]:
for i, c in enumerate('AUTO'):  # Operaator in pakib korteežid lahti.
    print(i, c)
0 A
1 U
2 T
3 O

Funktsioon isinstance¶

Funktsioon isinstance kontrollib objekti kuuluvuse andmetüüpi ning väljastab vastava booli tõeväärtuse. Funktsiooni kasutamise süntaks on järgmine:

isinstance(<obj>, class_or_tuple)

Sama info koos funktsiooni DocStringiga ehk abiinfoga:

In [35]:
isinstance?
Signature: isinstance(obj, class_or_tuple, /)
Docstring:
Return whether an object is an instance of a class or of a subclass thereof.

A tuple, as in ``isinstance(x, (A, B, ...))``, may be given as the target to
check against. This is equivalent to ``isinstance(x, A) or isinstance(x, B)
or ...`` etc.
Type:      builtin_function_or_method
In [36]:
isinstance(2, int)  # Proovi ka klasse list, tuple, jne.
Out[36]:
True
In [37]:
isinstance(2.0, float)
Out[37]:
True
In [38]:
num_type = (int, float, complex)

print(isinstance('Python', num_type))
print(isinstance(2.0, num_type))
print(isinstance(2, num_type))
False
True
True

Funktsioon int¶

Teatavasti meile juba tuttav konstruktorfunktsioon int loob või teisendab avaldisi või muid andmetüüpe täisarvudeks. Funktsiooni int saab kasutada ka arvude teisendamiseks erinevatest numbrisüsteemidest kümnendsüsteemi. Funktsiooni taolise kasutamise süntaks on järgmine:

int(<x>[, base=10])

kus positsionaalne vaikeväärtusega 10 argument base on teisendatava arvu baas. Sama info koos funktsiooni DocStringiga ehk abiinfoga:

In [39]:
int?
Init signature: int(self, /, *args, **kwargs)
Docstring:     
int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4
Type:           type
Subclasses:     bool, IntEnum, IntFlag, _NamedIntConstant
In [40]:
a = int('0b10', 0)       # Tõlgendab kui täisarvu. Töötab ka 10.
b = int('0b10', base=2)  # Töötab ka 10.
c = int('0o123', 8)      # Töötab ka 123.
d = int('0x121b', 16)    # Töötab ka 121b.
e = int('10', 10)

print(a, b, c, d, e)
2 2 83 4635 10

Funktsioon int toetab arvude aluseid vahemikus 2 kuni 36:

In [41]:
def example_vali_alus(b):
    if 2 <= b <= 36:
        arv = b - 1 
        print(f'Alusega {b} arv {arv} on kümnendsüsteemis {int(str(arv), b)}.')
    else:
        print('Alus peab olema vahemikus [2, 36]')

example_vali_alus(36)  # Aluseks valiti 36.
Alusega 36 arv 35 on kümnendsüsteemis 113.

Funktsioon eval¶

Funktsioon eval interpreteerib sellele sõnena esitatud avaldise ja väljastab selle väärtuse. Funktsiooni kasutamise süntaks on järgmine:

eval(<source>[, global[, locals]])

Sama info koos funktsiooni DocStringiga ehk abiinfoga:

In [42]:
eval?
Signature: eval(source, globals=None, locals=None, /)
Docstring:
Evaluate the given source in the context of globals and locals.

The source may be a string representing a Python expression
or a code object as returned by compile().
The globals must be a dictionary and locals can be any mapping,
defaulting to the current globals and locals.
If only globals is given, locals defaults to it.
Type:      builtin_function_or_method
In [43]:
eval('2 + 3')
Out[43]:
5
In [44]:
eval("print('2 + 3')")  # NB! Kui avaldis sisaldab jutumärke, kasuta erinevaid.
2 + 3
In [45]:
eval("abs(-1)", {"__builtins__": None})  # Eemaldati globaalsest skoobist sisseehitatud objektid.
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[45], line 1
----> 1 eval("abs(-1)", {"__builtins__": None})

File <string>:1

TypeError: 'NoneType' object is not subscriptable
In [46]:
eval("abs(-1)", {"__builtins__": None}, {"abs": abs})  # Lisame tagasi ainult funktsioooni abs.
Out[46]:
1

Lihtne kalkulaatoriprogramm kirjutatud kasutades funktsioone input ja eval:

In [47]:
tehe = input('Sisesta tehe:')
print(f'Tulemus: {tehe} = {eval(tehe)}.')
Tulemus: 1 + 2 = 3.

Funktsioon exec¶

Funktsioon exec interpreteerib sellele sõnes edastatud Pythoni koodi. Funktsiooni kasutamise süntaks on järgmine:

exec(<source>[, global[, locals]])

Sama info koos funktsiooni DocStringiga ehk abiinfoga:

In [48]:
exec?
Signature: exec(source, globals=None, locals=None, /, *, closure=None)
Docstring:
Execute the given source in the context of globals and locals.

The source may be a string representing one or more Python statements
or a code object as returned by compile().
The globals must be a dictionary and locals can be any mapping,
defaulting to the current globals and locals.
If only globals is given, locals defaults to it.
The closure must be a tuple of cellvars, and can only be used
when source is a code object requiring exactly that many cellvars.
Type:      builtin_function_or_method
In [49]:
exec('print("Hello World!")')  # NB! Kui avaldis sisaldab jutumärke, kasuta erinevaid.
Hello World!
In [50]:
code = 'a = 5 \nb = 10 \nprint("Sum =", a + b)'
exec(code)
Sum = 15
In [51]:
program = '''
for i in range(3):
    print("Hello World!")
    '''

exec(program)
Hello World!
Hello World!
Hello World!

Funktsioonid all ja any¶

Funktsioonid all ja any itereerivad üle iteraatortoega jada elementide ja rakendavad järjest paarikaupa operaatoreid and funktsioonis all ja or funktsioonis any nii kaua kuni leitakse vastus või kuni jada ammendub. Funktsioon all väljastab True kui kõik elemendid on tõesed või mittetriviaalsed ning funktsioon any väljastab True kui vähemelt üks element on tõene või mittetriviaalne.

Funktsioonide all ja any kasutamise süntaks on järgmine:

all(<iterable>)
any(<iterable>)

Sama info koos funktsiooni DocStringiga ehk abiinfoga:

In [52]:
all?
Signature: all(iterable, /)
Docstring:
Return True if bool(x) is True for all values x in the iterable.

If the iterable is empty, return True.
Type:      builtin_function_or_method
In [53]:
any?
Signature: any(iterable, /)
Docstring:
Return True if bool(x) is True for any x in the iterable.

If the iterable is empty, return False.
Type:      builtin_function_or_method
In [54]:
all([1, '', False])
Out[54]:
False
In [55]:
all([1, 'a', True])
Out[55]:
True
In [56]:
any([1, '', False])
Out[56]:
True
In [57]:
any([0, '', False, [], {}, None])
Out[57]:
False

Pesastatud funktsioon ja funktsiooni sulund (function closure)¶

Meeldetuletus: Kõrgemat järku funktsioonid võimaldavad vastuvõtta või väljastada funktsioone.
Tavaline pesastatud funktsioon. Väljastame pesastatud funktsiooni väärtuse teostades funktsiooni väljakutse peale lausendit return. Seega tegu pole kõrgemat järku funktsiooniga:

In [58]:
def väline():
    sõnum = 'Tere'
    def sise():  # Pesastatud funktsiooni definitsioon.
        print(sõnum)  # LEGB reegel.
    return sise()     # NB! Funktsiooni väärtus.

väline()
Tere

Funktsiooni sulund võimaldab siduda või sulustada andmeid pesastatud funktsiooniga. Hilisemad pesastatud funktsiooni väljakud omavad juurdepääsu sulustatud andmetele. Selline käitumine järeldub otseselt muutujate skoobist ja LEGB reeglist:

In [59]:
def väline():
    sõnum = 'Tere'    # Sulustatud andmed: sõne 'Tere'.
    def sise():
        print(sõnum)  # LEGB reegel.
    return sise       # NB! Väljastan funktsiooni, funk. on esimese klassi kodanik.

print(väline())  # Väljakutse väljastab funksiooni.

my_func = väline()    # my_func <-- sise

print(my_func)
print(my_func.__name__)  # Dunder atribuut.
my_func()  # Kapseldava skoobi sõne 'Tere' pole funktsiooni sise osa aga tuli sellega kaasa.
<function väline.<locals>.sise at 0x1277a8e00>
<function väline.<locals>.sise at 0x1277a8e00>
sise
Tere

Sulustatud sõne asub kapseldavas skoobis seega saame selle sulustada ka järgmiselt:

In [60]:
def väline(sõnum):  # Kaspeldav skoop.
    def sise():
        print(sõnum)  # LEGB reegel.
    return sise  # NB! Väljastan funktsiooni.

func = väline('Tere tudeng!')  # Sulustan kapseldavasse skoopi 'Tere tudeng!'.
func()
Tere tudeng!
In [61]:
def tervitus(tervita):
    def meenutus(keda):  # LEGB reegel.
        print(f'{tervita} {keda}')  # LEGB reegel.
    return meenutus

viisakus = tervitus('Tere')  # Sulustan 'Tere'.
viisakus('seltsimees')  # Edastan argumendi pesastatud funktsioonile meenutus.
Tere seltsimees

Näide: Funktsiooni sulundi kasutamine HTML koodi genereerimiseks. HTML dokumendi koodi kujul:

<h4>Juuliuse elu</h4>
<p>Juulius Tipikas sai eksamil viie.</p>

automatiseeritud loomine. Selline kood genereerib veebilehele järgmise väljundi mis sisaldab ühte neljanda astme pealkirja ja ühe lõiku teksti:

Juuliuse elu

Juulius Tipikas sai eksamil viie.

In [62]:
def html_tag(tag):
    def wrap_text(text):
        print('<{0}>{1}</{0}>'.format(tag, text))
    return wrap_text

pealkiri = html_tag('h4')  # Pealkirja tag.
par = html_tag('p')        # Teksti lõigu tag.

pealkiri('Juuliuse elu')
par('Juulius Tipikas sai eksamil viie.')
<h4>Juuliuse elu</h4>
<p>Juulius Tipikas sai eksamil viie.</p>

Funktsiooni dekoraator ja @ operaator¶

Dekoraator võimaldab olemasoleva funktsiooni käitumist muuta ilma funktsiooni definitsiooni ehk sisu muutmata. Dekoraator teeb seda sulundades funktsiooni.
Näide: Dekoraator mis muudab kaheargumendilise jagamistehtet teostava funktsiooni tehte järjekorda kui nimetaja on suurem lugejast:

In [63]:
def dekoraator(func):   
    def sise(a, b):  # Pesastatud funktsioon.
        if a < b:
            a, b = b, a
        return func(a, b)  # Sulustatud funktsiooni väljakutse.
    return sise  # Väljastame pesastatud funktsiooni mis omakorda väljastab sulustatud funktsiooni väärtuse.
            
def jaga(a, b):
    return a / b  # Lugeja / nimetaja.

print(dekoraator)
print(dekoraator(jaga))

jaga = dekoraator(jaga)  # NB! Dekoreerin funktsiooni jaga.

print(jaga(2, 4))  # Nimetaja on suurem.
<function dekoraator at 0x1277a9760>
<function dekoraator.<locals>.sise at 0x11106bd80>
2.0
In [64]:
jaga(4, 2)  # Nimetaja ei ole suurem.
Out[64]:
2.0

@ operaator¶

@ operaator võimaldab eelolevat kirjapilti lühendada:

In [65]:
# Syntactic sugar, lühem kirjapilt.
@dekoraator  # Asendab rida: jaga = dekoraator(jaga).
def jaga(a, b):
    return a / b

jaga(2, 4)  # Nimetaja on suurem.
Out[65]:
2.0

Rakenduse näide: puhvermälu (memoization)¶

Rekursiivse Fibonacci jada liikmeid $F(n)$, kus $n$ on järjekorranumber, leidva funktsiooni optimeerimine. Aeglane rekursiivne funktsioon on kujul:

In [66]:
def rec_F(n):
    '''Leiab n-da fibonacci jada liikme rekursiivselt.'''
    if n <= 2:
        return 1
    return rec_F(n - 2) + rec_F(n - 1)

rec_F(6)  # Aeglane ja sisuliselt kasutu kui n >> 1.
Out[66]:
8

Mäletatavasti võtab see funktsioon palju aega tulemuste leidmiseks kuna sisemiste funktsioonide väljakutsete arv suure $n$ puhul kasvab röögatult. Programm teostab $2 F(n) - 1$ rekursiivset funktsiooni väljakutset $n$-da jada liikme $F(n)$ arvutamiseks, vt. vrd. Joonis 1.

No description has been provided for this image
Joonis 1. Rekursiivne Fibonacci jada liikme $F(n)$, kus $n$ on täisarv, leidmine.

Kuna rekursiivne funktsioon teeb korduvaid funktsiooni väljakutseid (vt. Joonis 1) on kasulik eelnevalt leitud väljakutsete tulemused ajutiselt salvestada ning neid taaskasutada.

In [67]:
cache = {}  # Tühi sõnaraamat.
def puh_F(n):
    # Kui väärtus on mälus kasutame.
    if n in cache:
        return cache[n]  # Edasi ei lähe kui tulemus leitud.
    
    # Arvutame väärtuse.
    if n <= 2:
        tulem = 1
    else:
        tulem = puh_F(n - 2) + puh_F(n - 1)  # Rekursioon.
    
    # Salvesta tulem ja väljasta vastus.
    cache[n] = tulem  # Omistan sõnaraamatu võtmele n leitud väärtuse.
    return tulem
      
puh_F(35)
Out[67]:
9227465

Üldistame eeltoodud lahenduse luues dekoraatori. Eelnevalt leitud arvutustulemused saame sulustada funktsiooni sulundisse, ehk kasutada dekoraatorit kujul:

In [68]:
def cacheit(func):
    cache = {}  # Tühi sõnaraamat.
    def sisemine(n):
        tulem = cache.get(n)  # Kontrollin kas sõnaraamatus on tulem.
        if tulem is None:
             tulem = cache[n] = func(n)  # Lisan puuduva tulemuse sõnaraamatusse.
        return tulem
    return sisemine  # Väljastan funktsiooni.

dec_rec_F = cacheit(rec_F)  # Dekoreerin.
   
dec_rec_F(35)
Out[68]:
9227465

Sama tulemus kasutades @ operaatorit:

In [69]:
@cacheit  # Dekoreerin loodud dekoraaatoriga.
def alp_dec_rec_F(n):  # Uus funktsiooni definitsioon.
    if n <= 2:
        return 1
    return alp_dec_rec_F(n - 2) + alp_dec_rec_F(n - 1)

alp_dec_rec_F(146)
Out[69]:
1454489111232772683678306641953

Kui palju kiiremaks me funktsioonid tegime? Teostame analüüsi leides aja mis kulub 200 funktsiooni väljakutse teostamiseks. Leiame näiteks Fibonacci jada 30-da liikme:

In [70]:
import timeit  # Lausendit import pole veel õppinud.

def my_timeit(stmt, 
              setup='from __main__ import rec_F, puh_F, dec_rec_F, alp_dec_rec_F',
              number=200):
    aeg = timeit.timeit(stmt, setup=setup, number=number)
    print ('Calling {:>18} took\t {:.2f} us/execution'.format(stmt, 1e6*aeg/number))

my_timeit('rec_F(30)')       # rekursiivne,
my_timeit('puh_F(30)')        # bufferdatud, dekoreerimata.
my_timeit('dec_rec_F(30)')     # bufferdatud, dekoreeritud.
my_timeit('alp_dec_rec_F(30)')  # bufferdatud, dekoreeritud @ operaatoriga.
Calling          rec_F(30) took	 60314.52 us/execution
Calling          puh_F(30) took	 0.08 us/execution
Calling      dec_rec_F(30) took	 293.36 us/execution
Calling  alp_dec_rec_F(30) took	 0.07 us/execution

Jupiteri koodirakus ja iPythoni konsoolis saab interpreteerimisele kuulunud aega mõõta maagiliste käskudega %%timeit ja %%time:

In [71]:
%%timeit
rec_F(30)
58 ms ± 151 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [72]:
%%timeit
puh_F(30)
43 ns ± 0.0211 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
In [73]:
%%timeit
dec_rec_F(30)
57.2 ns ± 1.83 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
In [74]:
%%timeit
alp_dec_rec_F(30)
52.6 ns ± 0.275 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

Lausend assert ja erisus AssertionError¶

Lausend assert kontrollib kas etteantud tingimuse tõeväärtus on True. Juhul kui tingimus ei ole tõene peatatakse programm ja tõstatakse erisus AssertionError.

In [75]:
tingimus1 = True   # Ei juhtu midagi.
tingimus2 = False  # Tõstatakse erisus.

assert tingimus1, 'Teade 1.'
assert tingimus2, 'Teate 2.'
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Cell In[75], line 5
      2 tingimus2 = False  # Tõstatakse erisus.
      4 assert tingimus1, 'Teade 1.'
----> 5 assert tingimus2, 'Teate 2.'

AssertionError: Teate 2.
In [76]:
a = 1
b = 2
assert a < b, 'Muutuja b peab a-st suurem olema.'  # Programm ei peatu.

Programmi käivitamine konsoolis käsurealt¶

Pythoni programmifaili ehk mooduli (skripti) laiend on .py. Me saame enda koodi kirjutada mistahes tekstiredaktorisse ning salvestada loodud tekstifaili faililaiendiga ".py". Loodud tekstifaili saab edastada Pythoni interpretaatorile kasutades konsooli.

Ava Anaconda prompt (Windows) või terminal (macOS, UNIX ja Linux) leia enda loodud Pythoni moodul ehk programmifaili (.py fail). Käivita programm järgmise käsuga:

python <mooduli nimi>.py

Konsooli kasutamiseks piisab kõigest kolme käsu teadmisest. Konsoolis navigeerid failisüsteemi järgmiselt:

  • ls (MacOS, UNIX, Linux) või dir (Windows) - Kausta sisu järgivaatamine;
  • cd <alamkausta nimi> - Liigu alamkausta;
  • cd .. - Liigu tagasi, eelmine kaust.

Integreeritud arenduskeskkond, IDE (Integrated Development Environment)¶

IDE on tarkvara mis võimaldab programmeerijal enda tööd paremini organiseerida ja juhtida. IDEd on tavaliselt varustatud koodiredaktoriga, koodi kompileerimis- ja/või interpreteerimistööriistadega ning siluriga (degugger).

Siiamaani oleme koodi kirjutanud suhtelistel lihtsates arenduskeskkondades nimega Jupyter Notebook või Jupyter Lab (iPython). Integreeritud arenduskeskkonna tarkvara võimaldab kirjutada pikemaid programme mugavamalt. IDE lubab meil avada ja salvestada .py programmifaile ehk Pythoni mooduleid, omada paremat ülevaadet muutujate väärtustest, jpm.

Spyder integreeritud arenduskeskkonna tutvustus¶

Joonis 2 kujutab Spyder IDE graafilist kasutajaliidest. Koodiredaktroisse kirjutatud koodi saab interpreteerida kasutades graafilist liidest (nupud ekraanuil) või kasutades klaviatuuri: (Ctrl + Enter = interpreteeri kood ja jää koodiblokki või Shift + Enter = interpreteeri kood ja liigu järgmisesse koodiblokki. Spyderis defineerime uue koodibloki alguse kasutades #%%.

No description has been provided for this image
Joonis 2. Spyder IDE graafiline kasutajaliides, koodiredaktor, iPythoni konsool, abiinfo aken.

Juurdepääs projektifailidele on tagatud läbi failihalduri mis on kujutatud Joonisel 3.

No description has been provided for this image
Joonis 3. Spyder IDE graafiline kasutajaliides, failihaldur.

Loodud muutujate ja objektide väärtusi saab järgi vaadata muutujate lehitseja aknas mis on kujutatud Joonisel 4.

No description has been provided for this image
Joonis 4. Spyder IDE graafiline kasutajaliides, muutujate väärtuste aken.

Spyder projekti koduleht: https://www.spyder-ide.org/
Juhuslik Spyderi IDEd tutvustav video: https://www.youtube.com/watch?v=zYNRqVimU3Q

Teised integreeritud arenduskeskkonnad¶

Kui sa soovid või oled harjunud mõnda muud IDEd kastama võid loendutes ja ka eksami praktilise osa ajal seda kasutada. TalTechi tudengite seas on polulaarsed IDEd näiteks Visual Studio (info: https://visualstudio.microsoft.com) ja PyCharm (info ja TalTech tudengile pro versiooni litsents: https://www.jetbrains.com/student/).


Kursusetööst¶

Kursusetöö juhend ja näidis¶

Kursusetöö juhend ja näidis on leitav kursuse kodulehelt ja Moodlest. Viidatud näidis demonstreerib muuhulgas kursusetöö vähimat vastuvõetavat mahtu.

Tudengite hindamisest kursusel YFX0500 ja eksamist¶

Esimese loengu jooksul mainiti, et kursusehinne koosneb kolmest osast: kursusetööst mis on eksamile pääsemise eelduseks ja kaheosalisest eksamist:

  • Kursusetöö moodustab 5% kursusehindest, maksimaalselt on võimalik teenida kuni 5 pt.
  • Eksam moodustab 95% kursusehindest, maksimaalselt on võimalik saada kuni 95 pt.
    • Teoreetiline osa moodustab 45% kursusehindest, maksimaalselt on võimalik saada kuni 45 pt. Test toimub Moodles, testi saad võtta terve sessi jooksul. Testi sisu ja vormiga saab enne testi alustamist tutvuda.
    • Praktiline osa moodustab 50% kursuse hindest, maksimaalselt on võimalik saada kuni 50 pt. Toimub kohapeal. Kodulehelt ja Moodlest leiad praktiline osa näidise. Näidiseksam lahendatakse läbi viimase nädala praktikumis.
      • Ülesanne 1, süntaksireeglite peast tundmine: maksimaalselt kuni 5 pt.
      • Ülesanne 2, silumine ja koodi analüüs: maksimaalselt kuni 10 pt.
      • Ülesanne 3, algoritmi kasutus, andmete töötlus ja visualiseerimine: maksimaalselt kuni 35 pt.

Osa tudengitest teenivad ka lisapunkte või nn. brownie punkte loengutes ja praktikumides aktiivse osalemise eest. Need punktid (maksimaalselt kuni 10 p tudengi kohta) lisatakse kursusehinde punktisummale. Seega maksimaalselt on võimalik teenida kuni 100 + 10 = 110 p. Teenitud punktisummale vastava kursusehinde määratakse vastavalt kursuse kodulehel avaldatud hindamisskaalale.

















☻   ☻   ☻