Blog JSystems - z miłości do programowania

Funkcje



Funkcje przydadzą nam się wszędzie tam gdzie będziemy potrzebowali kodu wielokrotnego użytku. Funkcje mogą przyjmować parametry różnych typów, mogą też zwracać jakieś dane.


 


Deklarowanie funkcji


Najprostsza postać deklaracji funkcji:


def sayHello():
    print("Hello my friend!")


Funkcja nosi nazwę "sayHello".  Jej wywołanie sprowadza się do:


sayHello()


Należy tylko pamiętać że deklaracja funkcji musi znajdować się nad jej wywołaniem. Z tego powodu funkcje deklarujemy zwykle na początku pliku, lub w osobnym module (jeden z kolejnych tematów) który następnie importujemy na początku pliku.


 


Parametry funkcji


Parametry funkcji służą do przekazywania do niej danych.


def sayHello(imie):
    print("Hello my friend {}!".format(imie))


Powyżej przeróbka poprzedniego przykładu. Nie możemy posiadać w tym samym pliku dwóch funkcji o tej samej nazwie a różniącej się tylko liczbą parametrów. Przeciążanie tu nie funkcjonuje. Każda kolejna funkcja o takiej samej nazwie przesłania poprzednią.


Funkcje mogą przyjmować wiele parametrów, rozdzielamy je przecinkami:


def dodaj(x, y):
    print(x+y)


Funkcje z parametrami wywołujemy tak samo jak te bez parametrów, z tym że musimy podać wartości które do parametrów mają trafić:


dodaj(3,5)


Wartości parametrów można podmieniać wewnątrz funkcji - nie są tylko do odczytu jak w niektórych językach programowania. Przykładowo poniższa funkcja zawsze będzie witała Czesława, niezależnie od tego co podamy przy wywołaniu:


def sayHello(imie):
    imie="Czesław"
    print("Hello my friend {}!".format(imie))


Ponieważ zdeklarowane przez nas funkcje mogą być użytkowane przez inne osoby, a te niekoniecznie będą wiedziały jaki rodzaj danych nasza funkcja obsługuje, warto znać sposób na sprawdzenie typu danych które zostają podane przez parametry:


def sprawdz_typ(x):
    if( isinstance(x,int)): # sposób na sprawdzenie czy parametr jest spodziewanego typu
        print('otrzymalem liczbe calkowita')
    else:
        print('otrzymalem cos innego niz liczba calkowita')


W Pythonie możemy również zadeklarować wartość domyślną dla parametru funkcji:


def domyslne_wartosci(a="brak",b="brak"):
    print('a='+a)
    print('b='+b)


Przy wywołaniu możemy, ale nie musimy wtedy podawać wartości parametrów tej funkcji:


domyslne_wartosci("X","Y")


domyslne_wartosci()
domyslne_wartosci("coś")
domyslne_wartosci(b="coś innego")


Wynik na konsoli:


a=X


b=Y


a=brak


b=brak


a=coś


b=brak


a=brak


b=coś innego


W pierwszym przypadku wywołanie jak dotychczas. Chciałem jedynie zaznaczyć, że fakt posiadania przez funkcję wartości domyślnych parametrów nie powoduje że nie możemy jej wywoływać tak jak we wcześniejszych przykładach.


W drugim nie podaję wartości dla parametrów, a funkcja przyjmuje wartości domyślne.


Trzeci wariant jest bardzo ciekawy - co jeśli podamy mniej wartości niż parametrów? Python przypisze je do parametrów wg. kolejności, a pozostałe przyjmą wartości domyślne - ale tylko jeśli takie wartości zostaną zadeklarowane. Bez tego dostalibyśmy wyjątek.


Czwarta opcja to przekazywanie wartości do parametrów po nazwie.


 


Zwracanie wyników z funkcji


Funkcje mogą zwracać wartości. Mogą to być pojedyncze liczby czy ciągi tekstowe, ale również złożone struktury jak np. tablice. Najprostsza funkcja zwracająca "0":


def oddaj0():
    return 0


Wynik z takiej funkcji możemy odebrać i przekazać do innej funkcji (np. print), albo przypisać do jakiejś zmiennej:


print(oddaj0())
x=oddaj0()
print(x)


Przykład funkcji zwracającej złożony typ danych - listę cyfr od 0 do 9:


def dajcyferki():
    l=list(range(10))
    return l

dajcyferki()


 


Wyrażenia Lambda


Funkcje możemy deklarować w locie, najczęściej do jednorazowego użytku.  Wyrażenia Lambda są dużym i złożonym tematem - zwłaszcza ich zastosowanie, na ten moment w zupełności wystarczy nam się orientować co to takiego:


fun=lambda a,b: a+b
print (fun(10,20))  #wykorzystanie odebranego w ten sposob ciala funkcji.


Wyrażenie Lambda zwraca ciało funkcji. To musi być konkretnie wyrażenie, nie może tu byc np. print. Poniżej równoważna deklaracja zwykłej funkcji:


def nielambda(a,b):
    return a+b


 


Funkcja jako argument


Funkcja może być przekazana jako argument innej funkcji:


def razy2(a): # funkcja która będzie użyta jako argument
    return a*2


def funkcja_jako_argument(f,x):
    print(f(x))


funkcja_jako_argument(razy2,33)


Zadeklarowałem dwie funkcje. Pierwsza przyjmuje liczbę którą zwraca podwojoną. Druga funkcja przyjmuje dowolną funkcję "f" (ze względu na wywołanie w print musi ona posiadać jeden argument) oraz liczbę która zostanie podana do funkcji "f" w ciele funkcji "funkcja_jako_argument". Na końcu mamy wywołanie funkcji "funkcja_jako_argument", której efektem działania jest wypisanie na konsoli wartości "66".


Przekazujemy funkcję bez podania nawiasów - nie chcemy wywoływać tej funkcji przy przekazaniu a przekazać funkcję jako obiekt.


Poniżej przykład połączenia przekazywania funkcji jako argument i argumentów *args. Kod poniżej spowoduje wyświetlenie każdej z wartości *args po obróbce funkcją przekazaną jako argument. Funkcją "obrabiającą" jest tu funkcja powieksz która zwraca powiększony otrzymany przez argument tekst. Warunkiem działania poniższej kodu (ze względu na wywołanie x.upper()) jest podanie samych ciągów tekstowych do args:


def powieksz(x):
    return x.upper()

def zastosuj_dla_wszystkich(fun,*args):
    for a in args:
        print(fun(a))

zastosuj_dla_wszystkich(powieksz,'siała','baba','mak')


Wynik działania na konsoli:


SIAŁA


BABA


MAK


Funkcje mogą być przekazywane również jako listy. Poniższy przykład prezentuje zastosowanie rzędu funkcji na jednej zmiennej.


def pomnoz_razy_dwa(x):
    return x*2

def podziel_przez_trzy(x):
    return x/3

def dodaj_piec(x):
    return x+5

funkcje=[pomnoz_razy_dwa,podziel_przez_trzy,dodaj_piec]

def obrob(wartosc,*funkcs):
    for f in funkcs:
        wartosc=f(wartosc)
    return wartosc

print (obrob(1,pomnoz_razy_dwa,podziel_przez_trzy,dodaj_piec) )


Zdeklarowałem trzy proste funkcje. Ich nazwy wskazują co robią z otrzymaną wartością. Stworzyłem listę referencji do obiektów funkcji - zwróć uwagę że przekazuję funkcje bez podawania nawiasów a tym bardziej argumentów - ostatecznie chodzi o przekazanie referencji do funkcji a nie jej wywołanie. Wartość przekazana jako pierwszy argument zostaje obrobiona przez wszystkie funkcje podane jako *args (tutaj *funcs)  a następnie zwrócona z funkcji "obrob" i wyswietlona na konsoli. W efekcie działania dostajemy wartość "5.666666666666667".


 


Funkcja w funkcji


Możliwa jest deklaracja funkcji we wnętrzu innej funkcji. Taka funkcja wewnątrz innej funkcji będzie widziana tylko w niej:


def zewnetrzna(x):
    def wewnetrzna(x):
        return x*2
    print(wewnetrzna(x))

zewnetrzna(50)


 


Zwracanie funkcji z funkcji


Tworzone przez nas funkcje mogą zwracać zawarte w nich funkcje. Ponieważ funkcje są obiektami, możemy nie tylko przyjmować je przez argumenty, ale też zwracać. Poniżej obrazujący to przykład. Wewnątrz funkcji wybierz zdeklarowałem dwie funkcje. Jedna powiększa otrzymany tekst, druga go pomniejsza. W zależności od liczby którą podamy jako argument dla wywołania funkcji "wybierz" zostaje zwrócona jedna albo druga funkcja. Przy wywołaniu odbieram zwracaną funkcję i stosuję ją wobec wypisywanego tekstu:


def wybierz(tryb):
    def powieksz(x):
        return x.upper()
    def pomniejsz(x):
        return x.lower()
    if tryb==1:
        return powieksz
    elif tryb==2:
        return pomniejsz

funkcja=wybierz(1)
print(funkcja('Witaj Świecie!'))
funkcja=wybierz(2)
print(funkcja('Witaj Świecie!'))


Wynik na konsoli:


WITAJ ŚWIECIE!


witaj świecie!


Jeszcze jeden przykład:


Przeanalizujmy poniższy przykład:


def daj_funkcje(x):
    def podwoj(a):
        return a*2
    def polowa(a):
        return a/2
    if x==1:
        return podwoj
    elif x==2:
        return polowa

fun=daj_funkcje(1)
print ( fun(6) )


W wyniku uruchomienia powyższego kodu otrzymujemy na konsoli liczbę 12. Funkcja "daj_funkcje" zwraca jedną ze swoich wewnętrznych funkcji w zależności od otrzymanej przez argument wartości. Zwróć uwagę że mamy zapis "return podwoj" a nie "return podwoj()" - różnica jak zawsze tkwi w szczegółach. W pierwszym przypadku zwracamy obiekt funkcji, w drugim zwracamy wynik jej działania po wywołaniu.


 


Rekurencja


Rekurencja w dużym uproszczeniu sprowadza się do wywoływania funkcji przez samą siebie, aż do uzyskania określonego wyniku. Przykład z obliczaniem silni:


def silniaRek(n):
    if n==0:
        return 1
    else:
        return n*silniaRek(n-1)


Inny przykład rekurencji. Choć można wypisywanie wartości w wybranym zakresie zorganizować znacznie prościej, to tutaj użyłem do tego celu rekurencji:


def rekurencyjny_wypisywacz(n):
    print(n)
    if n>0:
        rekurencyjny_wypisywacz(n-1)
    else:
        return n
    return n

rekurencyjny_wypisywacz(10)


Po wywołaniu na konsoli  wyświetlane są liczby w zakresie 10-0 malejąco.

Przyjdź do nas na szkolenie z języka Python! Mamy szereg szkoleń w ofercie, od podstawowych po aplikacje webowe z użyciem Django, analizę danych, tesowanie, machine learning i wiele innych.
Sprawdź dostępne szkolenia Python
Zapisz się do newslettera aby otrzymywać najnowsze świeżynki pojawiające się na blogu!