Funktionen svarar

Att ge tillbaka ett värde

I Din första funktion definierade du funktioner med parametrar. Det räckte för att flytta kod till ett namngivet block och anropa det, men något saknas. Kör den här koden:

Editor

Redo.

Läs utskriften uppifrån. Först står det att skadan är 20, alltså har funktionen räknat rätt. Sedan kraschar programmet med TypeError på raden där skadan ska dras av, och felmeddelandet talar om NoneType.

Funktionen vet svaret. Den beräknade det korrekt och visade det för dig. Men den behöll det inuti sin stackram i stället för att skicka tillbaka det, så skada blev aldrig 20. Nyckelordet return Ett nyckelord som avslutar en funktion och skickar ett värde tillbaka till anroparen. Värdet till höger om return är det som placeras i variabeln på anropssidan. All kod efter return i samma exekveringsväg körs aldrig. Se: Funktionen svarar är det som lämnar värdet till Anroparen Den kod som anropar en funktion. Anroparen är den som tar emot returvärdet, det är till anroparen som return skickar tillbaka svaret. Se: Funktionen svarar , och utan det försvinner svaret när ramen plockas bort från stacken.

Lägg till raden return resultat sist i funktionen, indenterad som de andra, och kör igen. Nu räknar programmet färdigt.

None, det funktionen ger när den inte ger något

Värdet None Ett speciellt värde av typen NoneType som representerar frånvaron av ett värde. Alla funktioner som saknar ett return-uttryck returnerar None implicit. Att försöka utföra matematik eller strängoperationer på None ger TypeError. Se: Funktionen svarar är en egen datatyp i Python, NoneType. Den representerar frånvaron av ett värde: inte noll, inte en tom sträng, utan ingenting. Det är vad varje funktion utan return lämnar tillbaka:

Editor

Redo.

Funktionen gör sitt jobb, hälsningen syns. Ändå är svar lika med None, och type() bekräftar att den är av typen NoneType.

Det spelar ingen roll så länge funktionen bara ska göra något: skriva ut, visa ett meddelande, uppdatera skärmen. Problemet uppstår i samma sekund som du vill räkna vidare med resultatet. Prova: byt sista raden mot print(svar + "!") och kör. Python vägrar, för None går inte att lägga ihop med en sträng.

return, ge svaret tillbaka

return avslutar funktionen omedelbart och lämnar värdet till höger om nyckelordet tillbaka till anroparen. Här är de två varianterna sida vid sida:

Editor

Redo.

visa_skada skriver ut 20 men lämnar tillbaka None. beräkna_skada skriver inte ut något alls, men lämnar tillbaka 20, och därför är det bara det värdet du kan räkna vidare med.

Prova tre saker i koden. Skriv print(beräknad + 5) sist: det fungerar, du får 25. Skriv print(visad + 5): det kraschar. Lägg sedan in en rad efter return attack * 2, till exempel print("Klart!"), och kör igen. Den raden skrivs aldrig ut, för return avslutar funktionen på stället. Kod efter ett return är död kod, och Python säger ingenting om den.

Du kan returnera vad som helst: ett tal, en sträng, en lista, ett booleskt värde. Du kan också returnera ett uttryck direkt:

def är_besegrad(hp):
    return hp <= 0

if är_besegrad(fiende_hp):
    print("Fienden är besegrad!")

Det här är Valideringsfunktionen på Mönsterkortet: en funktion som kontrollerar ett villkor och svarar True eller False. Anroparen behöver inte veta hur kontrollen görs, bara vad svaret blev.

Beräknaren och presentatören

Tumregeln klarar de flesta situationer: vill du använda värdet vidare, returnera det. Vill du bara visa det för användaren, skriv ut det.

Det går att både returnera och skriva ut i samma funktion, men det är sällan bra design. Utskriften hör hemma i den kod som använder resultatet, inte i den som räknar ut det. Då kan samma funktion användas i ett textgränssnitt i dag och i ett fönster i morgon, utan att en enda rad inuti den behöver ändras.

Att en funktion ska gå att lita på utifrån vad den gör, inte hur, är en grundbult i modern programmering. Barbara Liskov formaliserade det tänkandet, att gömma detaljerna bakom ett tydligt gränssnitt, och fick Turingpriset för det.

Ett mönster du kommer att känna igen

tärning = random.randint(1, 6)

random.randint() är en funktion som räknar ut ett tal och returnerar det. Raden ovan tar emot ett returvärde och lagrar det, precis som dina egna funktioner nu kan göra. Hade randint() skrivit ut talet i stället för att returnera det hade tärning varit None.

Det heter Beräknaren på Mönsterkortet: ta in värden som parametrar, räkna, lämna tillbaka svaret. Det är ett av de mönster du kommer att känna igen om och om igen, både i din egen kod och i andras.

Uppgift: Beräkningsfunktioner för Brawl

Öppna din brawl/main.py.

  1. Skapa funktionen beräkna_skada(min_skada, max_skada) som använder random.randint() och returnerar ett skadevärde, utan att skriva ut det.

  2. Skapa funktionen är_vid_liv(hp) som returnerar True om hp är större än noll, False annars.

  3. Uppdatera spelloopen så att den använder båda funktionerna. Skadeutskrifterna hanteras av koden som anropar beräkna_skada(), inte av funktionen själv.

  4. Gå igenom samtliga funktioner i filen, även de du skrev i Din första funktion. Skriv en kommentar ovanför varje som anger om den är en Beräknare eller en Presentatör, och varför du valde det. Hittar du en som gör båda, dela den.

  5. Kör Brawl och verifiera att det fungerar identiskt som tidigare. Gå till Source Control-panelen i VS Code, stagea, skriv commit-meddelandet "Lade till beräkningsfunktioner med return" och pusha.

Vad händer om

Du tar bort return från beräkna_skada() men behåller en print() inuti den. Vilket felmeddelande får du, på vilken rad, och varför pekar Python på den raden snarare än på funktionsdefinitionen?

Motivera & reflektera

Motivera varför beräkningsfunktionerna returnerar sitt värde i stället för att skriva ut det. Vad vinner du på att låta anroparen bestämma vad som händer med svaret?