Nybörjafråga Python programmering

Trädvy Permalänk
Medlem
Plats
Torsås
Registrerad
Jan 2010

Nybörjafråga Python programmering

Testar och skriver ett enkelt program för att träna på + och - för sonen.

Dock har jag ett "problem", även om man svarar rätt så skriver den ut "FEL SVAR, FORSOK IGEN! RATT SVAR SKA VARA"

Jag har gjort en print av värdena för att kolla att variablarna är samma och de är det:
print z
print addans

ändå uppfylls inte

if addans == z: print ("RATT SVAR!")

Nån som kan förklara varför?

# Test Python Program print "********* TRANA MATEMATIK **********" ans = True addans = True while ans: print(""" 1. TRANA PA + (ADDITION) 2. TRANA PA - (SUBTRACTION) TRYCK PA 'ENTER' FOR ATT AVSLUTA """) ans = raw_input('VAD VILL DU GORA?') # VAL 1 if ans =="1": print("\nLYCKA TILL MED ++++++++++++") from random import randint from random import randint x = (randint(0,20)) y = (randint(0,20)) z = x + y print "VAD BLIR " ,x, " + " ,y, " ? " addans = raw_input() print z print addans if addans == z: print ("RATT SVAR!") else: print "FEL SVAR, FORSOK IGEN! RATT SVAR SKA VARA ",z, # VAL 2 elif ans =="2": print("\nLycka till med --------------- !") from random import randint from random import randint a = (randint(10,20)) b = (randint(0,9)) c = a - b print "VAD BLIR " ,a, " - " ,b, " ?" print c elif ans != "": print ("\nInget valbart alternativ.")

Trädvy Permalänk
Medlem
Registrerad
Jul 2011

Ja, dina värden addans och z är av olika typ. Specifikt så är addans = "3", dvs typen str (text) och z = 3, dvs typen int (heltal).

Du kan konvertera emellan dem med funktionerna str och int:

>>> int("3") 3 >>> str(3) '3'

Tänk på att all text inte kan konverteras till heltal och att "3" inte är lika med "03". Om du inte vill att ditt program ska krasha när du kör str("tre") så måste du använda felhantering, men det är ett mer komplicerat koncept.

Trädvy Permalänk
Forumledare
Registrerad
Okt 2002
Skrivet av Fredito:

Testar och skriver ett enkelt program för att träna på + och - för sonen.

Dock har jag ett "problem", även om man svarar rätt så skriver den ut "FEL SVAR, FORSOK IGEN! RATT SVAR SKA VARA"

Jag har gjort en print av värdena för att kolla att variablarna är samma och de är det:
print z
print addans

ändå uppfylls inte

if addans == z: print ("RATT SVAR!")

Nån som kan förklara varför?

# Test Python Program print "********* TRANA MATEMATIK **********" ans = True addans = True while ans: print(""" 1. TRANA PA + (ADDITION) 2. TRANA PA - (SUBTRACTION) TRYCK PA 'ENTER' FOR ATT AVSLUTA """) ans = raw_input('VAD VILL DU GORA?') # VAL 1 if ans =="1": print("\nLYCKA TILL MED ++++++++++++") from random import randint from random import randint x = (randint(0,20)) y = (randint(0,20)) z = x + y print "VAD BLIR " ,x, " + " ,y, " ? " addans = raw_input() print z print addans if addans == z: print ("RATT SVAR!") else: print "FEL SVAR, FORSOK IGEN! RATT SVAR SKA VARA ",z, # VAL 2 elif ans =="2": print("\nLycka till med --------------- !") from random import randint from random import randint a = (randint(10,20)) b = (randint(0,9)) c = a - b print "VAD BLIR " ,a, " - " ,b, " ?" print c elif ans != "": print ("\nInget valbart alternativ.")

Jag passar på att komma med några generella tips.

  • Indentering, dvs hur långt indrag från vänsterkanten man har i koden: konsekvent användning av detta är något som gör all kod mer lätthanterlig, och Python drar det till sin extrem och använder till och med denna för att tolka koden. Din kod fungerar, men gör dig en tjänst och indentera inte mer än nödvändigt.

  • Lite på samma tema: försök att vara noggrann med mellanrum mellan exempelvis operatorer, argument och annat. Om du är konsekvent med detta så blir det mycket lättare att hitta fel i koden, då det står ut som en neonskylt ifall något ser vajsing ut.

  • Idag finns det sällan någon anledning att koda för Python 2, vilket jag ser att du gjort genom att titta på den syntax du använder för print (ibland med parenteser, ibland utan; i Python 3 är det alltid parenteser som gäller) och raw_input (som döpts om till input i Python 3). Ändras de små detaljerna så har du ett fullfjädrat Python 3-program — halleluja! Då kan du också börja använda svenska tecken ([ÅÄÖåäö]) utan bekymmer, vilket jag ser att du brottats lite med än så länge. Se till att spara filen som UTF-8 så löser detta sig själv.

    "Konstiga tecken" kan man också använda i Python 2 genom att lägga till raden # -*- coding: utf-8 -*- i början av filen, men enklare är att lämna Python 2 bakom sig.

  • Du tjänar egentligen ingenting på att förkorta saker som variabelnamn i onödan; snarare tvärtom. Ju tydligare namn, desto lättare är det att snabbt läsa koden och få en bra överblick. Ibland kan korta variabelnamn vara självförklarande, men det finns ingen anledning att kalla en variabel för ans när answer duger lika bra. Du läser kod mycket oftare än du skriver kod under utveckling, och du sparar stora mängder tid på att undvika extra tolkningsarbete.

    Ofta kan logikproblem i program också bli smärtsamt tydliga ifall variabelnamnen är tydliga, och vice versa om de är otydliga. Det finns en sidodiskussion här om att det är olika viktigt i olika språk, men typiskt är tydliga variabelnamn extra viktigt i dynamiskt typade språk som Python.

Jag kopierar in en version av din kod nedan med viss hänsyn tagen till dessa saker (notera att den behöver köras med Python 3!), och några ytterligare kommentarer i koden. Jag väljer att använda fyra blanksteg för indentering snarare än tabb; det är en smaksak, men blanksteg brukar rekommenderas av olika anledningar. Har man en bra texteditor så sköter sig detta av sig själv. Har man inte en bra texteditor så bör man skaffa en .

#!/usr/bin/env python3 # Ovanstående rad är en standard för att ange vilken "tolk" (version av Python) # man vill att programmet ska köras med. Windows ignorerar generellt detta, men # även om man sitter på Windows så kan det hjälpa ens texteditor att veta # vilken Python-version det är som gäller, vilket gör att den kan hjälpa till # bättre med kodningen. Det är också bra att _själv_ snabbt kunna veta vilken # version av Python man siktar på, så jag rekommenderar att alltid skriva en # sådan rad. # Placera importer i början -- det är så mycket lättare att hitta dem då. # Notera också att du inte behöver importera samma sak flera gånger som du # gjorde tidigare: det räcker med en gång. from random import randint # Du behöver inte sätta ditt `addans` (mitt `answer`) innan loopen. choice = True print("********* TRÄNA MATEMATIK **********") while choice: print(""" 1. TRÄNA PÅ + (ADDITION) 2. TRÄNA PÅ - (SUBTRACTION) TRYCK PÅ 'ENTER' FOR ATT AVSLUTA """) choice = input('VAD VILL DU GÖRA?') if choice == "1": print("\nLYCKA TILL MED ++++++++++++") # Dina extra parenteser behövs inte här. a = randint(1, 20) b = randint(1, 20) answer = a + b print("VAD BLIR ", a, "+", b, " ?") # Ditt ursprungsproblem var att du jämförde en sträng som kom från # användaren mot den int du räknat ut. Om du "kastar" användarens input # till en int så kan du göra jämförelsen enkelt i ett senare tillfälle. # MEN: vad händer om användaren skriver "anka" i stället för något som # går att konvertera till en int på detta sätt..? Ett problem att lösa; # se "try except"-syntaxen i Python. user_answer = int(input()) if user_answer == answer: print("RÄTT SVAR!") else: print("FEL SVAR, FÖRSÖK IGEN! RÄTT SVAR SKA VARA ", answer) elif choice == "2": print("\nLycka till med --------------- !") a = randint(10, 20) b = randint(1, 9) answer = a - b print("VAD BLIR ", a, "-", b, " ?") user_answer = int(input()) if user_answer == answer: print("RÄTT SVAR!") else: print("FEL SVAR, FÖRSÖK IGEN! RÄTT SVAR SKA VARA ", answer) # Eftersom du vet att `choice` är en sträng så är `choice != ""` ekvivalent # med att kolla om `choice` är "sann" (du använder ju glatt det motsatta # som villkor för din `while`-sats). Sådana jämförelser föredras ofta då # det är mindre att läsa. elif choice: print("\nInget valbart alternativ.")

Dold text

Några kvarvarande bekymmer:

  • Du märker att det blir många upprepningar i koden. Du får skriva samma meddelande om och om igen, och många delar känns som "copy+paste" från andra delar, och skulle du lägga till fler räknesätt skulle du behöva kopiera stora kodstycken. Egentligen är det ju inte mycket som skiljer beräkningarna, förutom tecknet, så man kan misstänka att det finns ett sätt att låta datorn göra jobbet efter att man bara definierat tecknet själv.

    Man ska i idealfallet aldrig behöva kopiera kodstycken från ett ställe till ett annat utan modifiering ("don't repeat yourself"). Dels är det jobbigt, och kanske framför allt så är det så väldigt lätt att man upptäcker ett problem med koden på ett ställe, fixar det, men inte uppdaterar övriga ställen där samma kod står. Redan i program på bara ett tiotal rader kan detta blir ett problem. Vi kan notera inkonsekvens i ditt "Lycka till med"-meddelande som står med versaler på ett ställa men inte på ett annat.

    Detta kommer kanske bli extra tydligt om du lägger till felhantering för användarinput, då du kommer få en hel del logik att behöva kopiera runt i programmet ifall du inte lyckas bryta upp programmet i återanvändbara funktioner.

    Liknande problem löser man ofta genom att dela upp program i funktioner, och i förlängningen klasser och moduler. Det kan vara något att ha i bakhuvudet.

  • Felhantering i användarens input saknas, liksom ovanstående talare noterade och jag nämner i en kodkommentar ovan. Det är inte snyggt att krascha hela programmet bara för att användaren råkar skriva något som programmet inte har tänkt på att hantera.

  • Användaren blir nog snabbt trött på att behöva välja räknesätt efter varje tal, och inte få flera chanser på sig på varje enskilt tal.

Som bonusnummer så skrev jag ihop en alternativ variant av detta program (klass 1-varning för att det inte testats speciellt hårt). Tanken är inte att koden ska vara direkt pedagogiskt ämnad, men vissa saker kanske kan hjälpa; inte minst funktionen int_input som implementerar felhantering för användarinput genom att se till att användaren anger heltal.

Är det koncept i koden som du inte är van vid så är Google bra på att hjälpa, då den ofta pekar på den eminenta Pythonmanualen som bör vara huvudreferens när man vill veta hur Python jobbar (att lära sig använda den effektivt är en viktig del i att lära sig mer om språket). Du kan med gott samvete hoppa över saker som abc och klasser i nuläget — fortsätt att lägga fokus på att organiskt lösa de problem du själv stöter på när du skriver ditt program. Det är en väldigt bra början.

Framför allt är min tanke att du kan testköra programmet (i Python 3) för att få inspiration till upplägg för ditt eget program. Allt måste inte vara bra (en spontan tanke är att färgad output hade gjort programmet klart trevligare, och det borde delas upp i flera filer, men ligger i en enda för att enklare kunna testköras), men det kan åtminstone väcka tankar.

#!/usr/bin/env python3 """Repeatedly ask user randomized arithmetic questions.""" import abc import operator import random import sys class Arithmetic(metaclass=abc.ABCMeta): """Base class for arithmetic Q&A.""" @abc.abstractproperty def name(self): """Human readable name of the operation.""" @abc.abstractproperty def syntax(self): """Human readable operation string where {a} and {b} are replaced by the first and second expression parameter respectively.""" @abc.abstractproperty def op(self): """A function taking two int parameters and returning an int answer.""" @abc.abstractmethod def get_parameters(self): """Return integer parameters a and b for a sample question.""" def ask(self): """Ask for the answer of a generated question.""" expression, answer = self.question() print('\n>>>', expression) self.interaction(expression, answer) self.correct(expression, answer) def question(self): """Return a question as a readable expression and its answer.""" a, b = self.get_parameters() expression = self.syntax.format(a=a, b=b) answer = self.op(a, b) return expression, answer def interaction(self, expression, answer): """Return when the user answers the given expression correctly.""" while True: user_answer = int_input() if answer == user_answer: return else: self.incorrect() @staticmethod def correct(expression, answer): print('Korrekt! {} = {}'.format(expression, answer)) @staticmethod def incorrect(): print('Inte rätt. Försök igen!') class Addition(Arithmetic): """Arithmetic question handler for addition.""" name = 'Addition' syntax = '{a} + {b}' op = operator.add @staticmethod def get_parameters(): """Return integers a, b ∈ [1,20].""" return random.randint(1, 20), random.randint(1, 20) class Subtraction(Arithmetic): """Arithmetic question handler for subtraction.""" name = 'Subtraktion' syntax = '{a} − {b}' op = operator.sub @staticmethod def get_parameters(): """Return integers a ∈ [10, 20], b ∈ [1,10].""" return random.randint(10, 20), random.randint(1, 10) class Multiplication(Arithmetic): """Arithmetic question handler for multiplication.""" name = 'Multiplikation' syntax = '{a} ⋅ {b}' op = operator.mul @staticmethod def get_parameters(): """Return integers a, b ∈ [2, 12].""" return random.randint(2, 12), random.randint(2, 12) class Division(Arithmetic): """Arithmetic question handler for division.""" name = 'Division' syntax = '{a} / {b}' op = operator.floordiv @staticmethod def get_parameters(): """Return integers a, b describing even integer division a / b with a denominator and answer within [2, 12].""" denominator = random.randint(2, 12) numerator = denominator * random.randint(2, 12) return numerator, denominator def int_input(msg=''): """Ask user for input until an int is given, which is then returned.""" while True: value = input(msg) try: return int(value) except ValueError: print('"{}" är inte ett heltal. Försök igen!'.format(value)) def choose_arithmetic(arithmetics): """Choose list item via user menu with an exit option.""" choice_fmt = '{}: {}' for i, arithmetic in enumerate(arithmetics, start=1): print(choice_fmt.format(i, arithmetic.name)) print(choice_fmt.format(0, 'Avsluta')) try: while True: choice = int_input('Välj räknesätt: ') if choice == 0: quit() try: return arithmetics[choice - 1] except IndexError: print('Ogiltigt val. Försök igen!') except (KeyboardInterrupt, EOFError): quit() def quit(): """Exit the program with a user message.""" print('\nAvslutar programmet') sys.exit() def main(): arithmetics = [Addition(), Subtraction(), Multiplication(), Division()] intro_msg = 'Testa dig i de olika räknesätten!' print(intro_msg, '-' * len(intro_msg), sep='\n') while True: arithmetic = choose_arithmetic(arithmetics) print('\nÖvar "{}" (Ctrl-C för nytt val).'.format(arithmetic.name)) try: while True: arithmetic.ask() except (KeyboardInterrupt, EOFError): print('\nAvslutar övning.\n') continue if __name__ == '__main__': main()

Dold text

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Trädvy Permalänk
Medlem
Plats
Torsås
Registrerad
Jan 2010

Tack för svaren, ska testa igen sen när jag får tid.

Trädvy Permalänk
Medlem
Plats
Lund
Registrerad
Dec 2009

Mycket pedagogiskt och bra skrivet av phz!
Jag vill bara passa på att slå ett extra slag för kommentarer. Även om kommentarer i koden initialt kanske kännas onödiga i små korta program ökar de spårbarheten något enormt. Väldigt behändigt om du i framtiden skulle vilja återvända till den skrivna koden eller om någon annan snabbt skulle vilja sätta sig in i den.

PS. Vet inte vad du använder för editor (det finns en uppsjö, inklusive notepad) men ta ett titt på PyCharm.

Trädvy Permalänk
Medlem
Plats
Torsås
Registrerad
Jan 2010

@Auveno:
Ok ska skriva in lite mer kommentarer samt ska jag utöka programmet med mer funktioner, jag kör med Notepad ++ och kör python programmen på en raspberry pi.