Funktsioon, selle argumendid ning parameetrid

Iga mõistlik programm tegeleb kindla ülesande (probleemi) lahendamisega. Pisut suuremate programmide koostamisel on arukas selle jaotamine alamülesanneteks. Pythonis saab alamülesandeid kirjeldada programmiüksustes, mida nimetatakse funktsioonideks (Funktsiooni mõiste ja kasutamine). Lihtsustatult võib üht funktsiooni vaadelda kui koodi konteinerit, mis hoiab endas mingisugust koodi. Funktsiooni kirjeldamine algab võtmesõnaga def, millele järgneb funktsiooni nimi ning sulud. Sulud võivad olla tühjad või omada ühte või mitut parameetrit. Järgnevaid taandega esitatud ridu funktsiooni kirjelduses nimetatakse funktsiooni kehaks (function body).

Järgnev näide esitab parameetriteta funktsiooni "üldkuju":

# Function without parameters
def func_without_params():
    # Some code (function body)

Parameetritega funktsioon:

# Function with two parameters – a and b
def func_with_two_parameters(a, b):
    # Some code (function body)

Põhjused, miks kasutada funktsioone

  1. Keeruka probleemi jagamine (tükeldamine) mitmeks väikeseks "tükiks"

  2. Vähendamaks sama koodiploki korduvat esitamist

  3. Peitmaks programmi realisatsiooni detaile kasutaja eest

  4. Parandamaks koodi loetavust/jälgitavust

Argument ja parameeter

Funktsiooni kirjeldamise ja kasutamisega käivad (tihti) kaasas mõisted nagu argument ja parameeter. Parameetrid on eriliiki muutujad (introduction/variable) (nimelised kohad arvutimälus väärtuste salvestamiseks), mida kasutatakse andmete sissetoomiseks funktsiooni. Üks põhjus, miks parameetrit nimetatakse eriliiki muutujaks, seisneb selles, et see muutuja ei saa oma väärtust omistuslauses, nagu tavalise muutuja puhul. Parameetrid kirjeldatakse funktsiooni defineerimisel selle päisereas sulgude sees. Parameetrid saavad oma väärtused funktsiooni poole pöördumisel argumentidelt. Argumendiks võib olla konstant, muutuja, funktsiooniviide või avaldis. Võib öelda, et argumendid on funktsiooni sisendväärtused ehk väärtused, millega funktsiooni poole pöördutakse. Kui argumendiks on avaldis või funktsiooniviide, asutakse funktsiooni täitma alles siis, kui avaldise ja funktsiooniviite väärtused on leitud.

Attention

Kuigi avaldis koosneb tüüpiliselt operandidest, operaatoritest (tehtemärkidest) ja sulgudest, loetakse avaldiseks (erijuhuna) ka vaid konstanti, muutujat, funktsiooniviidet.

Vahel kasutatakse kirjanduses mõistete argument ja parameeter asemel mõisteid tegelik parameeter (actual parameter) ja formaalne parameeter (formal parameter).

Järgnevas näites on kirjeldatud kahe parameetriga funktsioon addition, mis liidab parameetrite a ja b väärtused ja prindib need konsoolaknas. Funktsiooni kirjeldus koosneb siin kahest reast: päisereast, mis algab võtmesõnaga def ja funktsiooni kehast, mis algab taandega.

# Here, a and b are called "parameters" (or "formal parameters")
def addition(a: int ,b: int):
    print(a + b)

# Here, 5 and 7 are called "arguments" (or "actual parameters")
addition(5, 7)  # --> Prints 12 to the console

Selleks, et kirjeldatud funktsioonist ka mingit kasu oleks, tuleb see välja kutsuda (käivitada). Täpsemalt, funktsiooni käivitamiseks tuleb selle poole pöörduda selle kirjelduses antud nimega ning argumentidega (kui funktsiooni kirjelduses on parameetrid esitatud). Eelnevas koodis esitab funktsiooni poole pöördumist (ka funktsiooni väljakutset) addition(5, 7). Kui programm asub rida (pöördumislauset) addition(5, 7) täitma antakse esimese argumendi väärtus 5 parameetrile a ja teise argumendi väärtus 7 parameetrile b.

def greeting():
    print("Welcome to python, pal.")

# If we want to execute the defined print(), we should call the function
greeting() # --> Prints "Welcome to python, pal." to the console

Pärast funktsiooni sees oleva koodi käivitamist funktsioon võib (aga ei pea) tagastada väärtuse - see võib olla number, sõne, list vms.

# A function which returns something
def func(parameters):
    # Some code
    return [expression]

Ja siis üleval olev näide on ümberkirjutatav kujul

def func() -> str:
    return "Welcome to python, pal."

# The function returns the string "Welcome to python, pal.", and we print it to the console
print(func()) # --> Welcome to python, pal.

Veel mõni näide. Olgu meil lihtne kalkulaator, mis liidab kokku kaks arvu:

# Variables
first_num = 5
second_num = 7

result = first_num + second_num

# Prints 12 to the console
print(result)

Ilma funktsioonita käivitub kood lihtsalt skripti käivitamisel. Kui aga paneme antud koodi funktsiooni calc() sisse, siis selleks, et funktsiooni sees olevat koodi käivitada, tuleb seda funktsiooni kutsuda:

# no pec # You can also edit the code def calc() -> int: """ Calculate the sum of 5 and 7. :return: sum of two given numbers """ # Variables first_num = 5 second_num = 7 result = first_num + second_num return result # Calling a function print(calc()) # --> 12

Interpretaator (interpreter) (programmikoodi tõlkiv ja täitev programm) vaatab algul pythoni (sise)funktsiooni print() sisse. Kuna print asub meie funktsiooni kirjeldusest väljaspool ning on ilma taaneteta, näeb interpretaator funktsiooni kutset ja pöördub selle poole. Pärast funktsiooni sees oleva koodi käivitamist, calc() tagastab kahe arvu summa, mis on antud juhul 12, ja meie prindime selle summa. Meie funktsiooni tagastatavaks väärtuseks on alati 12, kuna liidetavad arvud on määratud funktsiooni sees (5 ja 7) ja neid saab muuta ainult otseselt (panna näiteks first_num = 10, siis saame tulemuseks 10 + 7 = 17 vms).

See ei ole eriti mugav. Õnneks saab funktsioon sisse võtta ka argumente:

def calc(first_num: int, second_num: int) -> int:
    """
    Calculate the sum of two numbers.

    :param first_num: first number
    :param second_num: second number
    :return: sum of two given numbers
    """
    result = first_num + second_num

    return result

# Calling the function with two arguments.
print(calc(5, 7))  # Prints 12
print(calc(25, 39))  # Prints 64

Ehk põhimõtteliselt funktsiooni parameetrid on samad muutujad, millele antakse väärtused funktsiooni väljakutsumisel. Pöörake tähelepanu, et viimase näite korral IDE võib kuvada teile hoiatuse - local variable result is redundant. Tal on õigus (IDE always knows it better, just deal with it). Viisakas oleks ümber kirjutada meie funktsioon kujul:

# no pec def calc(first_num: int, second_num: int) -> int: """ Calculate the sum of two numbers. :param first_num: first number :param second_num: second number :return: sum of two given numbers """ return first_num + second_num # Calling the function with two arguments. print(calc(5, 7)) # Prints 12

Ehk result muutujat pole vaja, säästame veidi arvutimälu.

Üks funktsioon saab välja kutsuda (käivitada) ka teisi funktsioone. Näiteks, soovime, et meie kalkulaator liidaks kahe sisendarvu summale nende korrutise. Selleks loome eraldi funktsiooni:

# no pec # Creating a new function, which multiplies two numbers and returns the product def multiply(first_num: int, second_num: int) -> int: """ Multiply two numbers. :param first_num: first number :param second_num: second number :return: the result of multiplying two given numbers """ return first_num * second_num # Calling the multiply() function inside our main function calc(): def calc(first_num: int, second_num: int) -> int: """ Calculate the sum of two numbers and their multiplication. :param first_num: first number :param second_num: second number :return: sum of two numbers + their multiplication result """ return first_num + second_num + multiply(first_num, second_num) # Calling the function with two arguments print(calc(2, 3)) # --> 11 print(calc(5, 5)) # --> 35

Kui funktsioon tagastab mingi väärtuse, saab selle väärtuse salvestada muutujasse ja teha sellega kõike, mida on võimalik ühe muutujaga teha.

# no pec def repeat(word: str, times: int) -> str: """ Repeat a word amount. :param word: a word to repeat :param times: how many times parameter is repeated :return: string, where word is repeated times """ return word * times # Saving the return value into variable # People do so to make their code easier to read repeated_word = repeat("a", 3) print(repeated_word) # --> aaa # From technical point of view it isn't necessary to save the return value into variable print(repeat("abc", 4) == "abcabcabcabc") # --> True print(repeat("Python is easy. ", 1) + "Try Haskell") # --> Python is easy. Try Haskell

Vaikeparameeter

Funktsioonil võib olla ka nn vaikeparameeter, mis on algväärtustatud. See annab meile võimaluse funktsiooni välja kutsudes argument välja jätta. Sel juhul kasutatakse vaikimisi ette antud väärtust:

def hello(name="World"):
    """Greet someone."""
    print(f"Hello, {name}!")


# Calling the function
hello("Bob") # --> Hello, Bob!


# Calling the function without an argument
hello() # --> Hello, World!

Ettearvamatu hulk argumente (*args, **kwargs)

Tihti tuleb ette olukordi, kus soovime funktsioonile anda sisse erineva koguse argumente või nimelisi argumente (keyword arguments). Seega võib kohata funktsioonide definitsioonides järgnevat kirjapilti: def bacon(*args, **kwargs).

Vaatame kõigepealt *args (arguments) kasutust. Muutuja nimi ei pea tingimata olema args vaid võib vabalt olla ka midagi muud, args on lihtsalt kujunenud konventsiooniks. Tärni kasutus muutuja ees lubab saata funktsioonile määramata koguse (nimetuid) argumente.

Soovides luua funktsiooni, mis summeeriks sissetulevad arvud sõltumata kogusest, peaksime tegema funktsiooni kahe parameetriga, kolme parameetriga jne, aga * muutuja ees võimaldab seda mugavalt teha.

# no pec def sum_all(*numbers: int) -> int: total = 0 # numbers is a tuple containing all the positional arguments for numbers in numbers: total += numbers return total print(sum_all(45, 32, 89, 78)) # Prints 244 print(sum_all(2)) # Prints 2

Vaatame veel olukordi, kus lisaks positsioonilistele parameetritele on esimesel funktsioonil ka formaalne parameeter:

# no pec def multiply(initial: int, *args: int) -> int: result = initial for arg in args: result *= arg return result print(multiply(1, 2, 5)) # --> 10 print(multiply(0, 2)) # --> 0 # We can also use the same syntax to unpack numbers = (1, 2, 3) print(multiply(1, *numbers)) # --> 6
# no pec def print_all_args(*args): print(args) print_all_args(1) # --> (1,) print_all_args(1, 2) # --> (1, 2) print_all_args(5, 'cookie', [13, 14]) # --> (5, 'cookie', [13, 14])
# no pec def join_numbers(*numbers: int, separator=',') -> str: """Join numbers into a string using given separator or default comma.""" return separator.join(map(str, numbers)) print(join_numbers(1, 2, 3)) # --> 1,2,3 print(join_numbers(21, 10, 2018, separator='.')) # --> 21.10.2018 values = [3, 3] # With a variable print(join_numbers(*values, separator='|')) # --> 3|3 # Without a variable print(join_numbers(*(0, 2, 3))) # --> 0,2,3

**kwargs (keyword arguments) toimib analoogiliselt ja lubab määramata koguses nimelisi argumente. Samuti pole oluline muutuja nimi, aga muutuja ees peab olema **. Kui *args puhul oli parameetrina tegemist ennikuga (Ennik (tuple)), siis kwargs-i puhul luuakse sõnastik (Sõnastik (dict)). Vaatame näiteid:

# no pec def print_kwargs(**kwargs): print(kwargs) # A dictionary will be made (not ordered before Python 3.6) print_kwargs(name='Guido', age=62, is_cool=True) # Prints {'name': 'Guido', 'age': 62, 'is_cool': True}
# no pec def print_key_value_pairs(**kwargs): for key, value in kwargs.items(): print('Key: {}, Value: {}'.format(key, value)) print_key_value_pairs(attack=20, defense=12.5, hasEvolved=False) # Key: attack, Value: 20 # Key: defense, Value: 12.5 # Key: hasEvolved, Value: False # The same as above, but pass a dictionary player_data = {'attack': 20, 'defense': 12.5, 'hasEvolved': False} print_key_value_pairs(**player_data) # Key: attack, Value: 20 # Key: defense, Value: 12.5 # Key: hasEvolved, Value: False
# no pec def foo(arg, *args, kwarg_1=True, **kwargs): print(arg, args, kwarg_1, kwargs) # A lot of different ways to pass the same data foo('a', *(1, 2), kwarg_1=True, other_kwarg=True, y=1) foo('a', *(1, 2), other_kwarg=True, kwarg_1=True, y=1) foo('a', *(1, 2), other_kwarg=True, y=1) foo('a', *[1, 2], **dict(other_kwarg=True, y=1)) foo('a', *[1, 2], **dict([('other_kwarg', True), ('y', 1)])) kwargs = { 'other_kwarg': True, 'y': 1, } args = (1, 2) foo('a', *args, **kwargs) # a (1, 2) True {'other_kwarg': True, 'y': 1} is printed 6 times to the console # Some of these are for demonstration purposes, use the one that is most readable

Nüüd tasub vaadata Python3 sisseehitatud printimise funktsiooni deklaratsiooni ja aru saada kuidas see tegelikult toimib def print(self, *args, sep=' ', end='\n', file=None).

Rohkem lugemist: