Funkcje — program z klocków

🎯 Po co Ci to?

Twoje programy mają już po kilkanaście linijek. Prawdziwe mają po kilkaset tysięcy — i nikt nie ogarnia ich „od góry do dołu". Ogarnia się je tak, jak projektowałeś gazetkę w jednostce 1.4: krojąc na kawałki, z których każdy robi jedną rzecz i ma nazwę. W programowaniu taki nazwany kawałek to funkcja. Kilka funkcji już znasz (print, input, len, int) — dziś nauczysz się budować własne. To moment, w którym dekompozycja z działu 1 przestaje być rysunkiem, a staje się kodem.

✅ Czego się nauczysz

Po tej jednostce potrafisz:

  • definiować funkcje z parametrami i bez oraz wywoływać je wielokrotnie;
  • rozróżniać return od print — i wiedzieć, kiedy którego użyć;
  • dzielić program na funkcje tak, żeby główna część czytała się jak plan działania.

📘 Wyjaśnienie

Definicja i wywołanie. Funkcję definiuje się słowem def, nazwą i listą parametrów — danych, które dostaje do pracy:

def pole_prostokata(a, b):
    return a * b

print(pole_prostokata(3, 4))       # 12
print(pole_prostokata(2.5, 10))    # 25.0
x = pole_prostokata(5, 5) + 1      # z wynikiem można liczyć dalej: 26

Sama definicja niczego nie wykonuje — to spisanie przepisu do książki kucharskiej. Dopiero wywołanie pole_prostokata(3, 4) gotuje z przepisu: parametry a, b dostają wartości 3 i 4, ciało się wykonuje, a return zwraca wynik w miejsce wywołania. Jedna definicja, tysiąc użyć — na tym polega interes.

return to nie print. To najważniejsze rozróżnienie tej jednostki. return oddaje wartość programowi — do zmiennej, do warunku, do dalszych obliczeń. print pokazuje coś człowiekowi — i program nic z tego nie ma. Funkcja, która zamiast return robi print, jest jak kelner, który zamiast przynieść danie do stolika, pokazuje je gościom przez szybę: miło, ale nie da się tego zjeść.

def podwoj_zle(x):
    print(x * 2)          # pokazuje...

def podwoj_dobrze(x):
    return x * 2          # ...a to ODDAJE

wynik = podwoj_dobrze(21)     # wynik == 42, można liczyć dalej
cos = podwoj_zle(21)          # wypisze 42, ale cos jest puste (None)!

Funkcje logiczne. Wyjątkowo pożyteczna odmiana: funkcja zwracająca True/False. Nazywaj je jak pytania — kod zacznie czytać się jak proza:

def przestepny(rok):
    if rok % 400 == 0:
        return True
    if rok % 100 == 0:
        return False
    return rok % 4 == 0        # wynik porównania JEST wartością logiczną

if przestepny(2024):
    print("Luty ma 29 dni")

Dwie rzeczy warte spojrzenia: return natychmiast kończy funkcję (dlatego nie potrzeba elif — jeśli program dotarł do trzeciej linijki, tamte warunki na pewno nie zaszły), a ostatnia linijka zwraca wprost wynik porównania — rok % 4 == 0 już jest True albo False, opakowywanie go w if byłoby masłem maślanym.

Funkcje bez parametrów też bywają potrzebne — gdy kawałek pracy jest zawsze taki sam: def przywitaj(): z powitaniem na ekranie. Wywołuje się je z pustymi nawiasami: przywitaj(). Nawiasy są częścią wywołania — przywitaj bez nawiasów to tylko wskazanie na przepis, nie gotowanie.

Program z klocków. Spójrz, jak funkcje zmieniają architekturę — dziennik z poprzedniej jednostki, wersja dojrzała:

def wczytaj_oceny(ile):
    oceny = []
    for i in range(ile):
        oceny.append(int(input("Ocena: ")))
    return oceny

def srednia(lista):
    suma = 0
    for x in lista:
        suma = suma + x
    return suma / len(lista)

# --- program główny: czyta się jak PLAN, nie jak instrukcja obsługi ---
oceny = wczytaj_oceny(6)
print("Średnia:", srednia(oceny))

Główna część to dwie linijki mówiące co się dzieje; jak — siedzi w funkcjach. To jest metoda zstępująca i wstępująca z jednostki 1.6 w praktyce: planujesz z góry („wczytam, policzę, wypiszę"), budujesz z dołu (każdą funkcję z osobna da się przetestować, zanim złożysz całość).

🐞 Znajdź błąd

Funkcja ma zwracać większą z dwóch liczb:

def wieksza(a, b):
    if a > b:
        return a
    if b > a:
        return b

print(wieksza(7, 3))    # 7 — działa
print(wieksza(4, 4))    # ???

Co zwróci drugie wywołanie i jak to naprawić?

Sprawdź odpowiedź

Dla a == b żaden warunek nie zachodzi, funkcja kończy się bez return — a wtedy Python po cichu zwraca None („nic"). Wypisze się None, a rachunki na tym „niczym" wybuchną błędem. Dane brzegowe znów zaatakowały remisem — jak w dziale 1! Naprawa: drugi warunek zamienić na return b bez if (skoro nie a > b, to b jest większe lub równe — i w obu przypadkach b jest poprawną odpowiedzią). Reguła zawodowa: każda ścieżka przez funkcję zwracającą wartość musi kończyć się jakimś return.

⚠️ Uwaga, pułapka

Zmienne stworzone wewnątrz funkcji (w tym parametry) żyją tylko na czas jej wykonania — z zewnątrz ich nie widać. To nie złośliwość, to błogosławieństwo: funkcje nie wchodzą sobie nawzajem w szufladki, więc możesz w dwóch funkcjach spokojnie używać nazwy suma i nic się nie pogryzie. Jeśli funkcja ma coś przekazać światu — niech to zwróci.

🛠️ Teraz Ty

Rozbij na funkcje program BMI z jednostki 3.2: def bmi(masa, wzrost) zwraca wartość, def kategoria(wartosc) zwraca napis z klasyfikacją, a program główny tylko wczytuje dane i skleja wywołania. Potem — bez zmiany funkcji! — dopisz pętlę liczącą BMI dla trzech osób. Poczuj, jak klocki się opłacają.

📐 Definicje tej lekcji

  • Funkcja — nazwany fragment programu z parametrami; definiowana def, uruchamiana wywołaniem.
  • Parametr — dana wejściowa funkcji; przy wywołaniu dostaje konkretną wartość (argument).
  • return — zwrócenie wyniku i natychmiastowe zakończenie funkcji; brak return = zwrócone None.

📌 Najważniejsze w pigułce

  • Definicja to przepis, wywołanie to gotowanie; jedna definicja — wiele wywołań.
  • return oddaje wartość do dalszych obliczeń; print tylko pokazuje. Funkcje liczące mają zwracać.
  • Dobre funkcje robią jedną rzecz i nazywają się tak, że program główny czyta się jak plan.

🎒 Zadania

  1. Prześledź program i podaj, co wypisze — uwaga na kolejność wykonywania:
def f(x):
    return x + 10

def g(x):
    return f(x) * 2

print(g(5))
print(f(g(1)))
Wskazówka i odpowiedź

g(5): liczy f(5) = 15, razy 2 = 30. f(g(1)): najpierw wnętrze — g(1) = f(1)·2 = 22, potem f(22) = 32. Wypisze 30, potem 32. Funkcje wywołujące funkcje to chleb powszedni — a w dziale 7 zobaczysz funkcję, która wywołuje samą siebie.

  1. Napisz funkcję ile_samoglosek(napis), zwracającą liczbę samogłosek (a, e, i, o, u, y) w napisie. Wykorzystaj wzorzec licznika i sprawdź na słowie „algorytmika". Dodatkowo: jak obsłużyć wielkie litery?
Wskazówka i odpowiedź
def ile_samoglosek(napis):
    licznik = 0
    for znak in napis:
        if znak in "aeiouy":
            licznik = licznik + 1
    return licznik

(znak in "aeiouy" sprawdza, czy znak występuje w napisie samogłosek — wygodny pythonowy idiom.) „algorytmika": a-o-y-i-a → 5. Wielkie litery: sprawdzaj znak in "aeiouyAEIOUY" — albo elegancko obniż cały napis przed pętlą; o gotowych narzędziach do tego typu zabiegów powiemy przy bibliotekach (3.8).

  1. Kolega napisał def srednia(lista): z print zamiast return i dziwi się, że if srednia(oceny) > 4.0: wybucha błędem. Wyjaśnij mu w trzech zdaniach, co się dzieje i jak naprawić.
Wskazówka i odpowiedź

Twoja funkcja pokazuje średnią na ekranie, ale programowi zwraca None — bo bez return Python zwraca „nic". Warunek próbuje porównać None > 4.0, a „niczego" nie da się porównywać z liczbą — stąd błąd. Zamień print na return, a wypisywanie zostaw temu, kto funkcję wywołuje: liczenie i pokazywanie to dwie różne odpowiedzialności.

🔍 Sprawdź, czy umiesz

  • Napisać funkcję z parametrami, wywołać ją trzykrotnie z różnymi argumentami i użyć wyniku w obliczeniu.
  • Wyjaśnić różnicę return/print metaforą i przykładem kodu.
  • Przerobić 20-linijkowy program „z góry na dół" na trzy funkcje + krótki program główny.

Ucz się tej jednostki z asystentem