Erstellen Sie eine Python-Multiplikationstabellen-App mit OOP

In diesem Artikel werden Sie eine Multiplikationstabellen-App erstellen, indem Sie die Leistungsfähigkeit der objektorientierten Programmierung (OOP) in Python nutzen.

Sie üben die Hauptkonzepte von OOP und deren Verwendung in einer voll funktionsfähigen Anwendung.

Python ist eine Multiparadigmen-Programmiersprache, was bedeutet, dass wir als Entwickler die beste Option für jede Situation und jedes Problem auswählen können. Wenn wir über objektorientierte Programmierung sprechen, beziehen wir uns auf eines der am häufigsten verwendeten Paradigmen zum Erstellen skalierbarer Anwendungen in den letzten Jahrzehnten.

Die Grundlagen von OOP

Wir werden einen kurzen Blick auf das wichtigste Konzept von OOP in Python werfen, die Klassen.

Eine Klasse ist eine Vorlage, in der wir die Struktur und das Verhalten von Objekten definieren. Diese Vorlage ermöglicht es uns, Instanzen zu erstellen, die nichts anderes sind als einzelne Objekte, die nach der Zusammensetzung der Klasse erstellt werden.

Eine einfache Buchklasse mit den Attributen Titel und Farbe würde wie folgt definiert.

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

Wenn wir Instanzen des Klassenbuchs erstellen wollen, müssen wir die Klasse aufrufen und ihr Argumente übergeben.

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

Eine gute Darstellung unseres aktuellen Programms wäre:

Das Tolle ist, dass wir „Book“ erhalten, wenn wir den Typ der blue_book- und green_book-Instanzen überprüfen.

# Printing the type of the books

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

Nachdem diese Konzepte kristallklar sind, können wir mit dem Aufbau des Projekts beginnen 😃.

Projektaussage

Während der Arbeit als Entwickler/Programmierer wird die meiste Zeit nicht damit verbracht, Code zu schreiben dernewstack Wir verbringen nur ein Drittel unserer Zeit damit, Code zu schreiben oder umzugestalten.

Die anderen zwei Drittel verbrachten wir damit, den Code anderer zu lesen und das Problem zu analysieren, an dem wir arbeiten.

Für dieses Projekt erstelle ich also eine Problemstellung und wir analysieren, wie wir daraus unsere App erstellen können. Als Ergebnis machen wir den kompletten Prozess, vom Nachdenken über die Lösung bis zur Anwendung mit Code.

Ein Grundschullehrer möchte ein Spiel, um die Multiplikationsfähigkeiten von Schülern im Alter von 8 bis 10 Jahren zu testen.

Das Spiel muss ein Leben- und ein Punktesystem haben, bei dem der Schüler mit 3 Leben beginnt und eine bestimmte Anzahl von Punkten erreichen muss, um zu gewinnen. Das Programm muss eine „Verlieren“-Meldung anzeigen, wenn der Schüler sein ganzes Leben verbraucht.

Das Spiel muss zwei Modi haben, zufällige Multiplikationen und Tabellenmultiplikationen.

Die erste sollte dem Schüler eine zufällige Multiplikation von 1 bis 10 geben, und er/sie muss richtig antworten, um einen Punkt zu gewinnen. Geschieht dies nicht, verliert der Schüler ein Leben und das Spiel geht weiter. Der Schüler gewinnt nur, wenn er 5 Punkte erreicht.

Der zweite Modus muss eine Multiplikationstabelle von 1 bis 10 anzeigen, in die der Schüler das Ergebnis der jeweiligen Multiplikation eingeben muss. Wenn der Schüler dreimal durchfällt, verliert er/sie, aber wenn er/sie zwei Tische komplettiert, endet das Spiel.

Ich weiß, dass die Anforderungen vielleicht etwas größer sind, aber ich verspreche dir, dass wir sie in diesem Artikel lösen werden 😁.

Teile und herrsche

Die wichtigste Fähigkeit beim Programmieren ist das Problemlösen. Dies liegt daran, dass Sie einen Plan haben müssen, bevor Sie mit dem Hacken des Codes beginnen können.

Ich schlage immer vor, das größere Problem zu nehmen und es in kleinere zu unterteilen, die sowohl einfach als auch effizient gelöst werden können.

Wenn Sie also ein Spiel erstellen müssen, brechen Sie es zunächst in die wichtigsten Teile davon auf. Diese Teilprobleme werden viel einfacher zu lösen sein.

Nur dann haben Sie die Klarheit, wie Sie alles mit Code ausführen und integrieren können.

Lassen Sie uns also ein Diagramm erstellen, wie das Spiel aussehen würde.

Diese Grafik stellt die Beziehungen zwischen den Objekten unserer App her. Wie Sie sehen können, sind die beiden Hauptobjekte die zufällige Multiplikation und die Tabellenmultiplikation. Und das einzige, was sie gemeinsam haben, sind die Attribute Punkte und Leben.

Lassen Sie uns mit all diesen Informationen in den Code einsteigen.

Erstellen der übergeordneten Spielklasse

Wenn wir mit objektorientierter Programmierung arbeiten, suchen wir nach dem saubersten Weg, Codewiederholungen zu vermeiden. Das nennt man TROCKEN (wiederhole dich nicht).

Hinweis: Dieses Ziel bezieht sich nicht darauf, weniger Codezeilen zu schreiben (die Codequalität darf nicht an diesem Aspekt gemessen werden), sondern die am häufigsten verwendete Logik zu abstrahieren.

Nach der vorherigen Idee muss die Elternklasse unserer Anwendung die Struktur und das gewünschte Verhalten der anderen beiden Klassen festlegen.

Mal sehen, wie es gemacht werden würde.

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, das scheint eine ziemlich große Klasse zu sein. Lassen Sie es mich ausführlich erklären.

Lassen Sie uns zunächst die Klassenattribute und den Konstruktor verstehen.

Grundsätzlich sind Klassenattribute Variablen, die innerhalb der Klasse, aber außerhalb des Konstruktors oder einer beliebigen Methode erstellt werden.

Während Instanzattribute Variablen sind, die nur innerhalb des Konstruktors erstellt werden.

Der Hauptunterschied zwischen diesen beiden ist der Umfang. dh Klassenattribute sind sowohl von einem Instanzobjekt als auch von der Klasse aus zugänglich. Auf Instanzattribute hingegen kann nur von einem Instanzobjekt aus zugegriffen werden.

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

Ein weiterer Artikel kann tiefer in dieses Thema eintauchen. Bleiben Sie in Kontakt, um es zu lesen.

Die Funktion get_numeric_input wird verwendet, um zu verhindern, dass der Benutzer Eingaben bereitstellt, die nicht numerisch sind. Wie Sie vielleicht bemerken, ist diese Methode so konzipiert, dass sie den Benutzer fragt, bis er eine numerische Eingabe erhält. Wir werden es später im Unterricht der Kinder verwenden.

Die Druckmethoden ermöglichen es uns, uns das wiederholte Drucken derselben Sache zu ersparen, wenn ein Ereignis im Spiel eintritt.

Zu guter Letzt ist die Methode run nur ein Wrapper, den die Klassen Random multiplication und Table multiplication verwenden, um mit dem Benutzer zu interagieren und alles funktionsfähig zu machen.

Erstellen der Klassen des Kindes

Sobald wir diese übergeordnete Klasse erstellt haben, die die Struktur und einige der Funktionen unserer App festlegt, ist es an der Zeit, die eigentlichen Spielmodusklassen zu erstellen, indem Sie die Macht der Vererbung nutzen.

Zufällige Multiplikationsklasse

Diese Klasse führt den „ersten Modus“ unseres Spiels aus. Es wird natürlich das Zufallsmodul verwenden, das uns die Möglichkeit gibt, den Benutzer nach Zufallsoperationen von 1 bis 10 zu fragen. Hier ist ein ausgezeichneter Artikel über das Zufallsmodul (und andere wichtige Module) 😉.

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()

Hier ist eine weitere massive Klasse 😅. Aber wie ich bereits sagte, es ist nicht die Anzahl der Zeilen, die es braucht, sondern wie gut lesbar und effizient es ist. Und das Beste an Python ist, dass es Entwicklern ermöglicht, sauberen und lesbaren Code zu erstellen, als würden sie normales Englisch sprechen.

Diese Klasse hat eine Sache, die Sie vielleicht verwirren kann, aber ich werde es so einfach wie möglich erklären.

    # 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)

Der Konstruktor der Kindklasse ruft die Superfunktion auf, die gleichzeitig auf die Elternklasse (BaseGame) verweist. Es sagt Python im Grunde:

Füllen Sie das Attribut „points_to_win“ der übergeordneten Klasse mit 5 aus!

Es ist nicht notwendig, self innerhalb des super().__init__()-Teils zu platzieren, nur weil wir super innerhalb des Konstruktors aufrufen, und dies zu Redundanz führen würde.

Wir verwenden auch die super-Funktion in der run-Methode und werden sehen, was in diesem Codeabschnitt passiert.

    # 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()
        
        .....

Wie Sie vielleicht die run-Methode in der übergeordneten Klasse bemerken, geben Sie die Begrüßungs- und Beschreibungsnachricht aus. Es ist jedoch eine gute Idee, diese Funktionalität beizubehalten und auch zusätzliche in den untergeordneten Klassen hinzuzufügen. Dementsprechend verwenden wir super, um den gesamten Code der übergeordneten Methode auszuführen, bevor wir das nächste Stück ausführen.

Der andere Teil der Run-Funktion ist ziemlich einfach. Es fragt den Benutzer nach einer Nummer mit der Nachricht der Operation, auf die er antworten muss. Dann wird das Ergebnis mit der echten Multiplikation verglichen und wenn sie gleich sind, wird ein Punkt hinzugefügt, wenn sie nicht 1 Leben abziehen.

Es ist erwähnenswert, dass wir While-Else-Schleifen verwenden. Dies würde den Rahmen dieses Artikels sprengen, aber ich werde in wenigen Tagen einen darüber veröffentlichen.

Schließlich verwendet get_random_numbers die Funktion random.randint, die eine zufällige Ganzzahl innerhalb des angegebenen Bereichs zurückgibt. Dann gibt es ein Tupel aus zwei zufälligen ganzen Zahlen zurück.

Zufällige Multiplikationsklasse

Der „zweite Modus“ muss das Spiel in einem Einmaleins-Format anzeigen und sicherstellen, dass der Benutzer mindestens 2 Tabellen richtig beantwortet.

Zu diesem Zweck werden wir wieder die Macht von super nutzen und das übergeordnete Klassenattribut points_to_win auf 2 ändern.

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()

Wie Sie erkennen können, ändern wir nur die run-Methode dieser Klasse. Das ist die Magie der Vererbung, wir schreiben einmal die Logik, die wir verwenden, an mehreren Stellen und vergessen sie 😅.

In der run-Methode verwenden wir eine for-Schleife, um die Zahlen von 1 bis 10 abzurufen und die Operation zu erstellen, die dem Benutzer angezeigt wird.

Wenn die Leben erschöpft sind oder die zum Gewinnen erforderlichen Punkte erreicht sind, wird die While-Schleife erneut unterbrochen und die Gewinn- oder Verlustmeldung wird angezeigt.

JA, wir haben die beiden Modi des Spiels erstellt, aber bis jetzt passiert nichts, wenn wir das Programm ausführen.

Lassen Sie uns also das Programm fertigstellen, indem wir die Modusauswahl implementieren und die Klassen in Abhängigkeit von dieser Auswahl instanziieren.

Umsetzung der Wahl

Der Benutzer kann wählen, welchen Modus er spielen möchte. Mal sehen, wie man es umsetzt.

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()

Zuerst bitten wir den Benutzer, zwischen den Modi 1 oder 2 zu wählen. Wenn die Eingabe nicht gültig ist, wird das Skript nicht mehr ausgeführt. Wenn der Benutzer den ersten Modus auswählt, führt das Programm den Zufallsmultiplikationsspielmodus aus, und wenn er/sie den zweiten auswählt, wird der Tischmultiplikationsmodus ausgeführt.

So würde es aussehen.

Fazit

Herzlichen Glückwunsch, Sie gerade Erstellen Sie eine Python-App mit objektorientierter Programmierung.

Der gesamte Code ist in der verfügbar Github-Repository.

In diesem Artikel haben Sie gelernt:

  • Verwenden Sie Python-Klassenkonstruktoren
  • Erstellen Sie eine funktionierende App mit OOP
  • Verwenden Sie die super-Funktion in Python-Klassen
  • Wenden Sie die grundlegenden Konzepte der Vererbung an
  • Implementieren Sie Klassen- und Instanzattribute

Viel Spaß beim Programmieren 👨‍💻

Erkunden Sie als Nächstes einige der besten Python-IDEs für eine bessere Produktivität.