Bygg en Python Multiplikasjonstabell-app med OOP

I denne artikkelen skal du bygge en multiplikasjonstabell-app ved å bruke kraften til objektorientert programmering (OOP) i Python.

Du vil øve på hovedkonseptene til OOP, og hvordan du bruker dem i en fullt funksjonell applikasjon.

Python er et multiparadigme programmeringsspråk, noe som betyr at vi som utviklere kan velge det beste alternativet for hver situasjon og problem. Når vi snakker om objektorientert programmering, refererer vi til et av de mest brukte paradigmene for å bygge skalerbare applikasjoner, de siste tiårene.

Grunnleggende om OOP

Vi skal ta en rask titt på det viktigste konseptet til OOP i Python, klassene.

En klasse er en mal der vi definerer strukturen og oppførselen til objekter. Den malen lar oss lage forekomster, som ikke er annet enn individuelle objekter laget etter sammensetningen av klassen.

En enkel bokklasse, med attributtene tittel og farge, vil bli definert som følger.

class Book:
    def __init__(self, title, color):
        self.title = title
        self.color = color

Hvis vi vil lage forekomster av klasseboken, må vi ringe klassen og sende argumenter til den.

# Instance objects of Book class
blue_book = Book("The blue kid", "Blue")
green_book = Book("The frog story", "Green")

En god representasjon av vårt nåværende program vil være:

Det fantastiske er at når vi sjekker typen blue_book og green_book-forekomster, får vi «Book».

# Printing the type of the books

print(type(blue_book))
# <class '__main__.Book'>
print(type(green_book))
# <class '__main__.Book'>

Etter å ha fått disse konseptene krystallklare, kan vi begynne å bygge prosjektet 😃.

Prosjekterklæring

Mens du jobber som utviklere/programmerere, går ikke mesteparten av tiden til å skrive kode, ifølge thenewstack vi bruker bare en tredjedel av tiden vår på å skrive eller refaktorisere kode.

Vi brukte de andre to tredjedeler på å lese andres kode og analysere problemet vi jobber med.

Så for dette prosjektet vil jeg generere en problemstilling, og vi vil analysere hvordan vi lager appen vår fra den. Som et resultat gjør vi hele prosessen, fra å tenke på løsningen til å bruke den med kode.

En primærlærer ønsker et spill for å teste multiplikasjonsferdighetene til elever fra 8 til 10 år.

Spillet må ha et liv og et poengsystem, hvor eleven starter med 3 liv og må nå et visst antall poeng for å vinne. Programmet må vise en «taper»-melding hvis studenten tømmer hele livet.

Spillet må ha to moduser, tilfeldige multiplikasjoner og tabellmultiplikasjoner.

Den første skal gi eleven en tilfeldig multiplikasjon fra 1 til 10, og han/hun må svare riktig for å vinne et poeng. Hvis det ikke skjer, mister studenten en live og spillet fortsetter. Eleven vinner først når hun/han når 5 poeng.

Den andre modusen må vise en multiplikasjonstabell fra 1 til 10, hvor eleven må legge inn resultatet av den respektive multiplikasjonen. Hvis eleven mislykkes 3 ganger taper han/hun, men hvis hun/han fullfører to bord, er spillet ferdig.

Jeg vet at kravene kanskje er litt større, men jeg lover deg at vi løser dem i denne artikkelen 😁.

Splitt og hersk

Den viktigste ferdigheten i programmering er problemløsning. Dette er fordi du må ha en plan før du begynner å begynne å hacke koden.

  Hvordan Apple Watch kan hjelpe i en nødssituasjon

Jeg foreslår alltid å ta det større problemet og dele det opp i mindre som kan løses både enkelt og effektivt.

Så hvis du trenger å lage et spill, start med å dele det inn i de viktigste delene av det. Disse delproblemene vil være mye lettere å løse.

Akkurat da kan du få klarhet i hvordan du utfører og integrerer alt med kode.

Så la oss lage en graf over hvordan spillet vil se ut.

Denne grafikken etablerer relasjonene mellom objektene i appen vår. Som du kan se er de to hovedobjektene tilfeldig multiplikasjon og tabellmultiplikasjon. Og det eneste de deler er attributtene Points og Lives.

Med all denne informasjonen i tankene, la oss gå inn i koden.

Oppretting av foreldrespillklassen

Når vi jobber med objektorientert programmering, søker vi etter den reneste måten å unngå koderepetisjon. Dette kalles TØRKE (ikke gjenta deg selv).

Merk: Dette målet er ikke relatert til å skrive færre linjer med kode (kodekvalitet må ikke måles etter det aspektet), men å abstrahere den mest brukte logikken.

I henhold til den forrige ideen må den overordnede klassen til applikasjonen vår etablere strukturen og ønsket oppførsel til de to andre klassene.

La oss se hvordan det vil bli gjort.

class BaseGame:

    # Lenght which the message is centered
    message_lenght = 60
    
    description = ""    
        
    def __init__(self, points_to_win, n_lives=3):
        """Base game class

        Args:
            points_to_win (int): the points the game will need to be finished 
            n_lives (int): The number of lives the student have. Defaults to 3.
        """
        self.points_to_win = points_to_win

        self.points = 0
        
        self.lives = n_lives

    def get_numeric_input(self, message=""):

        while True:
            # Get the user input
            user_input = input(message) 
            
            # If the input is numeric, return it
            # If it isn't, print a message and repeat
            if user_input.isnumeric():
                return int(user_input)
            else:
                print("The input must be a number")
                continue     
             
    def print_welcome_message(self):
        print("PYTHON MULTIPLICATION GAME".center(self.message_lenght))

    def print_lose_message(self):
        print("SORRY YOU LOST ALL OF YOUR LIVES".center(self.message_lenght))

    def print_win_message(self):
        print(f"CONGRATULATION YOU REACHED {self.points}".center(self.message_lenght))
        
    def print_current_lives(self):
        print(f"Currently you have {self.lives} livesn")

    def print_current_score(self):
        print(f"nYour score is {self.points}")

    def print_description(self):
        print("nn" + self.description.center(self.message_lenght) + "n")

    # Basic run method
    def run(self):
        self.print_welcome_message()
        
        self.print_description()

Wow, dette virker som en ganske stor klasse. La meg forklare det i dybden.

Først av alt, la oss forstå klasseattributtene og konstruktøren.

I utgangspunktet er klasseattributter variabler opprettet i klassen, men utenfor konstruktøren eller en hvilken som helst metode.

Mens forekomstattributter er variabler opprettet kun inne i konstruktøren.

Hovedforskjellen mellom disse to er omfanget. dvs. klasseattributter er tilgjengelige både fra et forekomstobjekt og klassen. På den annen side er instansattributter bare tilgjengelige fra et instansobjekt.

game = BaseGame(5)

# Accessing game message lenght class attr from class
print(game.message_lenght) # 60

# Accessing the message_lenght class attr from class
print(BaseGame.message_lenght)  # 60

# Accessing the points instance attr from instance
print(game.points) # 0

# Accesing the points instance attribute from class
print(BaseGame.points) # Attribute error

En annen artikkel kan dykke dypere inn i dette emnet. Hold kontakten for å lese den.

Get_numeric_input-funksjonen brukes til å hindre brukeren i å gi inndata som ikke er numeriske. Som du kanskje legger merke til er denne metoden designet for å spørre brukeren til den får en numerisk inndata. Vi vil bruke det senere i barnets timer.

  Hvordan endre Apple ID-passord på iPhone

Utskriftsmetodene lar oss lagre repetisjonen av å skrive ut det samme hver gang en hendelse inntreffer i spillet.

Sist, men ikke minst, er kjøremetoden bare en innpakning som klassene Random multiplikasjon og Tabellmultiplikasjon vil bruke for å samhandle med brukeren og gjøre alt funksjonelt.

Opprette barnets klasser

Når vi har opprettet den overordnede klassen, som etablerer strukturen og noe av funksjonaliteten til appen vår, er det på tide å bygge de faktiske spillmodusklassene ved å bruke kraften i arv.

Tilfeldig multiplikasjonsklasse

Denne klassen vil kjøre den «første modusen» i spillet vårt. Den kommer til å bruke den tilfeldige modulen selvfølgelig, som vil gi oss muligheten til å spørre brukeren om tilfeldige operasjoner fra 1 til 10. Her er en utmerket artikkel om de tilfeldige (og andre viktige moduler) 😉.

import random # Module for random operations
class RandomMultiplication(BaseGame):

    description = "In this game you must answer the random multiplication correctlynYou win if you reach 5 points, or lose if you lose all your lives"

    def __init__(self):
        # The numbers of points needed to win are 5
        # Pass 5 "points_to_win" argument
        super().__init__(5)

    def get_random_numbers(self):

        first_number = random.randint(1, 10)
        second_number = random.randint(1, 10)

        return first_number, second_number
        
    def run(self):
        
        # Call the upper class to print the welcome messages
        super().run()
        

        while self.lives > 0 and self.points_to_win > self.points:
            # Gets two random numbers
            number1, number2 = self.get_random_numbers()

            operation = f"{number1} x {number2}: "

            # Asks the user to answer that operation 
            # Prevent value errors
            user_answer = self.get_numeric_input(message=operation)

            if user_answer == number1 * number2:
                print("nYour answer is correctn")
                
                # Adds a point
                self.points += 1
            else:
                print("nSorry, your answer is incorrectn")

                # Substracts a live
                self.lives -= 1
            
            self.print_current_score()
            self.print_current_lives()
            
        # Only get executed when the game is finished
        # And none of the conditions are true
        else:
            # Prints the final message
            
            if self.points >= self.points_to_win:
                self.print_win_message()
            else:
                self.print_lose_message()

Her er en annen massiv klasse 😅. Men som jeg sa før, er det ikke antallet linjer det tar, det er hvor mye lesbart og effektivt det er. Og det beste med Python er at det lar utviklere lage ren og lesbar kode som om de snakket vanlig engelsk.

Denne timen har én ting som kan forvirre deg, men jeg skal forklare det så enkelt som mulig.

    # Parent class
    def __init__(self, points_to_win, n_lives=3):
        "...
    # Child class
    def __init__(self):
        # The numbers of points needed to win are 5
        # Pass 5 "points_to_win" argument
        super().__init__(5)

Konstruktøren av barneklassen kaller superfunksjonen som samtidig refererer til den overordnede (BaseGame) klassen. Det er i utgangspunktet å fortelle Python:

Fyll ut «points_to_win»-attributtet til foreldreklassen med 5!

Det er ikke nødvendig å sette seg selv inne i super().__init__()-delen bare fordi vi kaller super inne i konstruktøren, og det ville resultere i redundant.

Vi bruker også superfunksjonen i kjøremetoden, og vi vil se hva som skjer i den kodebiten.

    # Basic run method
    # Parent method
    def run(self):
        self.print_welcome_message()
        
        self.print_description()
    def run(self):
        
        # Call the upper class to print the welcome messages
        super().run()
        
        .....

Som du kanskje legger merke til kjøremetoden i foreldreklassen, skriv ut velkomst- og beskrivelsesmeldingen. Men det er en god idé å beholde den funksjonaliteten og også legge til ekstra i barneklassene. I følge det bruker vi super for å kjøre all koden til overordnet metode før vi kjører neste del.

  Det er enkelt å kjøre lønn for din voksende virksomhet med Zoho Payroll

Den andre delen av løpsfunksjonen er ganske grei. Den ber brukeren om et nummer med meldingen om operasjonen han/hun må svare. Deretter sammenlignes resultatet med den virkelige multiplikasjonen, og hvis de er like, legger de til et poeng, hvis de ikke tar av 1 liv.

Det er verdt å si at vi bruker while-else-løkker. Dette overskrider omfanget av denne artikkelen, men jeg vil publisere en om det om noen dager.

Til slutt, get_random_numbers, bruker funksjonen random.randint, som returnerer et tilfeldig heltall innenfor det angitte området. Deretter returnerer den en tuppel av to tilfeldige heltall.

Tilfeldig multiplikasjonsklasse

«Andre modus» må vise spillet i et multiplikasjonstabellformat, og sørge for at brukeren svarer riktig på minst 2 tabeller.

For det formålet vil vi igjen bruke kraften til super og endre overordnet klasseattributtet points_to_win til 2.

class TableMultiplication(BaseGame):

    description = "In this game you must resolve the complete multiplication table correctlynYou win if you solve 2 tables"
    
    def __init__(self):
        # Needs to complete 2 tables to win
        super().__init__(2)

    def run(self):

        # Print welcome messages
        super().run()

        while self.lives > 0 and self.points_to_win > self.points:
            # Gets two random numbers
            number = random.randint(1, 10)            

            for i in range(1, 11):
                
                if self.lives <= 0:
                    # Ensure that the game can't continue 
                    # if the user depletes the lives

                    self.points = 0
                    break 
                
                operation = f"{number} x {i}: "

                user_answer = self.get_numeric_input(message=operation)

                if user_answer == number * i:
                    print("Great! Your answer is correct")
                else:
                    print("Sorry your answer isn't correct") 

                    self.lives -= 1

            self.points += 1
            
        # Only get executed when the game is finished
        # And none of the conditions are true
        else:
            # Prints the final message
            
            if self.points >= self.points_to_win:
                self.print_win_message()
            else:
                self.print_lose_message()

Som du skjønner, endrer vi bare kjøremetoden for denne klassen. Det er magien med arv, vi skriver en gang logikken vi bruker på flere steder, og glemmer det 😅.

I kjøremetoden bruker vi en for-løkke for å få tallene fra 1 til 10 og bygget operasjonen som vises til brukeren.

Nok en gang hvis livene er oppbrukt eller poengene som trengs for å vinne er nådd, vil while-løkken bryte, og vinn eller tap-meldingen vil vises.

JA, vi har laget de to modusene i spillet, men til nå vil ingenting skje hvis vi kjører programmet.

Så la oss fullføre programmet ved å implementere modusvalget, og instansiere klassene avhengig av det valget.

Valg gjennomføring

Brukeren vil kunne velge hvilken modus som vil spille. Så la oss se hvordan du implementerer det.

if __name__ == "__main__":

    print("Select Game mode")

    choice = input("[1],[2]: ")

    if choice == "1":
        game = RandomMultiplication()
    elif choice == "2":
        game = TableMultiplication()
    else:
        print("Please, select a valid game mode")
        exit()

    game.run()

Først ber vi brukeren velge mellom 1 eller 2 moduser. Hvis inngangen ikke er gyldig, slutter skriptet å kjøre. Hvis brukeren velger den første modusen, vil programmet kjøre spillmodusen tilfeldig multiplikasjon, og hvis han/hun velger den andre, kjøres tabellmultiplikasjonsmodusen.

Her er hvordan det vil se ut.

Konklusjon

Gratulerer, bare du bygge en Python-app med objektorientert programmering.

All koden er tilgjengelig i Github-depot.

I denne artikkelen lærte du å:

  • Bruk Python-klassekonstruktører
  • Lag en funksjonell app med OOP
  • Bruk superfunksjonen i Python-klasser
  • Bruk de grunnleggende begrepene arv
  • Implementer klasse- og instansattributter

Lykke til med koding 👨‍💻

Utforsk deretter noen av de beste Python IDE for bedre produktivitet.