Δημιουργήστε μια εφαρμογή πίνακα πολλαπλασιασμού Python με το OOP

Σε αυτό το άρθρο, πρόκειται να δημιουργήσετε μια εφαρμογή πίνακες πολλαπλασιασμού, χρησιμοποιώντας τη δύναμη του Αντικειμενοστρεφούς Προγραμματισμού (OOP) στην Python.

Θα εξασκηθείτε στις κύριες έννοιες του OOP και πώς να τις χρησιμοποιήσετε σε μια πλήρως λειτουργική εφαρμογή.

Η Python είναι μια πολυπαραδειγματική γλώσσα προγραμματισμού, που σημαίνει ότι εμείς ως προγραμματιστές μπορούμε να επιλέξουμε την καλύτερη επιλογή για κάθε κατάσταση και πρόβλημα. Όταν μιλάμε για Αντικειμενοστραφή Προγραμματισμό, αναφερόμαστε σε ένα από τα πιο χρησιμοποιούμενα παραδείγματα για την κατασκευή κλιμακούμενων εφαρμογών, τις τελευταίες δεκαετίες.

Τα βασικά του OOP

Θα ρίξουμε μια γρήγορη ματιά στην πιο σημαντική έννοια του OOP στην Python, τις κλάσεις.

Μια κλάση είναι ένα πρότυπο στο οποίο ορίζουμε τη δομή και τη συμπεριφορά των αντικειμένων. Αυτό το πρότυπο μάς επιτρέπει να δημιουργήσουμε Στιγμιότυπα, τα οποία δεν είναι παρά μεμονωμένα αντικείμενα που δημιουργούνται σύμφωνα με τη σύνθεση της κλάσης.

Μια απλή κλάση βιβλίου, με τα χαρακτηριστικά του τίτλου και του χρώματος, θα ορίζεται ως εξής.

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

Εάν θέλουμε να δημιουργήσουμε παρουσίες του βιβλίου κλάσης, πρέπει να καλέσουμε την κλάση και να της περάσουμε ορίσματα.

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

Μια καλή αναπαράσταση του τρέχοντος προγράμματός μας θα ήταν:

Το φοβερό είναι ότι όταν ελέγχουμε τον τύπο των περιπτώσεων blue_book και green_book, παίρνουμε “Book”.

# Printing the type of the books

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

Αφού έχουμε αυτές τις έννοιες κρυστάλλινα, μπορούμε να αρχίσουμε να χτίζουμε το έργο 😃.

Δήλωση έργου

Ενώ εργάζεστε ως προγραμματιστές/προγραμματιστές, τον περισσότερο χρόνο δεν ξοδεύετε γράφοντας κώδικα, σύμφωνα με thenewsstack ξοδεύουμε μόνο το ένα τρίτο του χρόνου μας γράφοντας ή αναδημιουργώντας κώδικα.

Ξοδέψαμε τα άλλα δύο τρίτα διαβάζοντας τον κώδικα του άλλου και αναλύοντας το πρόβλημα που εργαζόμαστε.

Έτσι, για αυτό το έργο, θα δημιουργήσω μια δήλωση προβλήματος και θα αναλύσουμε πώς να δημιουργήσουμε την εφαρμογή μας από αυτήν. Ως αποτέλεσμα, κάνουμε την πλήρη διαδικασία, από το να σκεφτόμαστε τη λύση μέχρι να την εφαρμόσουμε με κώδικα.

Ένας δάσκαλος πρωτοβάθμιας εκπαίδευσης θέλει ένα παιχνίδι για να δοκιμάσει τις δεξιότητες πολλαπλασιασμού μαθητών από 8 έως 10 ετών.

Το παιχνίδι πρέπει να έχει ζωές και σύστημα πόντων, όπου ο μαθητής ξεκινά με 3 ζωές και πρέπει να φτάσει σε ένα συγκεκριμένο αριθμό πόντων για να κερδίσει. Το πρόγραμμα πρέπει να εμφανίζει ένα μήνυμα «χαλάς» εάν ο μαθητής εξαντλήσει όλη του τη ζωή.

Το παιχνίδι πρέπει να έχει δύο τρόπους, τυχαίους πολλαπλασιασμούς και πολλαπλασιασμούς πίνακα.

Το πρώτο πρέπει να δώσει στον μαθητή έναν τυχαίο πολλαπλασιασμό από το 1 έως το 10 και πρέπει να απαντήσει σωστά για να κερδίσει έναν βαθμό. Εάν αυτό δεν συμβεί, ο μαθητής χάνει ένα live και το παιχνίδι συνεχίζεται. Ο μαθητής κερδίζει μόνο όταν φτάσει τους 5 βαθμούς.

Η δεύτερη λειτουργία πρέπει να εμφανίζει έναν πίνακα πολλαπλασιασμού από το 1 έως το 10, όπου ο μαθητής πρέπει να εισάγει το αποτέλεσμα του αντίστοιχου πολλαπλασιασμού. Αν ο μαθητής αποτύχει 3 φορές χάνει, αλλά αν συμπληρώσει δύο τραπέζια, το παιχνίδι τελειώνει.

Ξέρω ότι οι απαιτήσεις μπορεί να είναι λίγο μεγαλύτερες, αλλά σας υπόσχομαι ότι θα τις λύσουμε σε αυτό το άρθρο 😁.

Διαίρει και βασίλευε

Η πιο σημαντική δεξιότητα στον προγραμματισμό είναι η επίλυση προβλημάτων. Αυτό οφείλεται στο γεγονός ότι πρέπει να έχετε ένα σχέδιο προτού αρχίσετε να κάνετε εισβολή στον κώδικα.

  Πώς να ανακτήσετε τον παλιό σας λογαριασμό Xbox One

Πάντα προτείνω να παίρνετε το μεγαλύτερο πρόβλημα και να το χωρίζετε σε μικρότερα που μπορούν να λυθούν εύκολα και αποτελεσματικά.

Έτσι, αν χρειάζεται να δημιουργήσετε ένα παιχνίδι, ξεκινήστε χωρίζοντάς το στα πιο σημαντικά μέρη του. Αυτά τα υποπροβλήματα θα είναι πολύ πιο εύκολο να λυθούν.

Ακριβώς τότε μπορείτε να έχετε τη σαφήνεια του πώς να εκτελέσετε και να ενσωματώσετε τα πάντα με κώδικα.

Ας κάνουμε λοιπόν ένα γράφημα για το πώς θα μοιάζει το παιχνίδι.

Αυτό το γραφικό καθορίζει τις σχέσεις μεταξύ των αντικειμένων της εφαρμογής μας. Όπως μπορείτε να δείτε τα δύο κύρια αντικείμενα είναι ο τυχαίος πολλαπλασιασμός και ο πολλαπλασιασμός πίνακα. Και το μόνο πράγμα που μοιράζονται είναι τα χαρακτηριστικά Points και Lives.

Έχοντας υπόψη όλες αυτές τις πληροφορίες, ας μπούμε στον κώδικα.

Δημιουργία της τάξης παιχνιδιών γονέων

Όταν εργαζόμαστε με αντικειμενοστραφή προγραμματισμό, αναζητούμε τον πιο καθαρό τρόπο για να αποφύγουμε την επανάληψη κώδικα. Αυτό ονομάζεται ΣΤΕΓΝΟΣ (μην επαναλαμβάνεσαι).

Σημείωση: Αυτός ο στόχος δεν σχετίζεται με τη σύνταξη των λιγότερων γραμμών κώδικα (η ποιότητα του κώδικα δεν πρέπει να μετριέται με αυτήν την πτυχή) αλλά με την αφαίρεση της πιο χρησιμοποιούμενης λογικής.

Σύμφωνα με την προηγούμενη ιδέα, η γονική κλάση της εφαρμογής μας πρέπει να καθορίσει τη δομή και την επιθυμητή συμπεριφορά των άλλων δύο κλάσεων.

Ας δούμε πώς θα γίνει.

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

Ουάου, αυτό φαίνεται μια αρκετά τεράστια κατηγορία. Επιτρέψτε μου να το εξηγήσω σε βάθος.

Πρώτα απ ‘όλα, ας κατανοήσουμε τα χαρακτηριστικά της κλάσης και τον κατασκευαστή.

Βασικά, τα χαρακτηριστικά κλάσης είναι μεταβλητές που δημιουργούνται μέσα στην κλάση, αλλά έξω από τον κατασκευαστή ή οποιαδήποτε μέθοδο.

Ενώ τα χαρακτηριστικά του instance είναι μεταβλητές που δημιουργούνται μόνο μέσα στον κατασκευαστή.

Η κύρια διαφορά μεταξύ αυτών των δύο είναι το εύρος. δηλ. τα χαρακτηριστικά κλάσης είναι προσβάσιμα τόσο από ένα αντικείμενο παρουσίας όσο και από την κλάση. Από την άλλη πλευρά, τα χαρακτηριστικά του στιγμιότυπου είναι προσβάσιμα μόνο από ένα αντικείμενο παρουσίας.

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

Ένα άλλο άρθρο μπορεί να εμβαθύνει σε αυτό το θέμα. Μείνετε σε επαφή για να το διαβάσετε.

Η συνάρτηση get_numeric_input χρησιμοποιείται για να εμποδίσει τον χρήστη να παρέχει οποιαδήποτε είσοδο που δεν είναι αριθμητική. Όπως μπορείτε να παρατηρήσετε αυτή η μέθοδος έχει σχεδιαστεί για να ρωτά τον χρήστη μέχρι να λάβει μια αριθμητική είσοδο. Θα το χρησιμοποιήσουμε αργότερα στις τάξεις του παιδιού.

  Πιστοποίηση Microsoft Power BI Data Analyst (PL-300): Πώς να το αποκτήσετε

Οι μέθοδοι εκτύπωσης μας επιτρέπουν να αποθηκεύουμε την επανάληψη της εκτύπωσης του ίδιου πράγματος κάθε φορά που συμβαίνει ένα συμβάν στο παιχνίδι.

Τελευταίο αλλά εξίσου σημαντικό, η μέθοδος εκτέλεσης είναι απλώς ένα περιτύλιγμα που θα χρησιμοποιήσουν οι κλάσεις τυχαίου πολλαπλασιασμού και πολλαπλασιασμού πίνακα για να αλληλεπιδράσουν με τον χρήστη και να κάνουν τα πάντα λειτουργικά.

Δημιουργία τάξεων του παιδιού

Μόλις δημιουργήσουμε αυτήν τη γονική κλάση, η οποία καθορίζει τη δομή και ορισμένες από τις λειτουργίες της εφαρμογής μας, ήρθε η ώρα να δημιουργήσουμε τις πραγματικές κλάσεις λειτουργίας παιχνιδιού, χρησιμοποιώντας τη δύναμη της κληρονομικότητας.

Κλάση τυχαίου πολλαπλασιασμού

Αυτή η τάξη θα τρέξει την “πρώτη λειτουργία” του παιχνιδιού μας. Θα χρησιμοποιήσει φυσικά την τυχαία ενότητα, η οποία θα μας δώσει τη δυνατότητα να ζητάμε από τον χρήστη τυχαίες λειτουργίες από το 1 έως το 10. Εδώ είναι ένα εξαιρετικό άρθρο για το τυχαίο (και άλλες σημαντικές ενότητες) 😉.

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

Εδώ είναι άλλη μια τεράστια τάξη 😅. Αλλά όπως είπα και πριν, δεν είναι ο αριθμός των γραμμών που χρειάζεται, αλλά το πόσο ευανάγνωστο και αποτελεσματικό είναι. Και το καλύτερο πράγμα για την Python είναι ότι επιτρέπει στους προγραμματιστές να δημιουργούν καθαρό και ευανάγνωστο κώδικα σαν να μιλούν κανονικά αγγλικά.

Αυτή η τάξη έχει ένα πράγμα που μπορεί να σας μπερδέψει, αλλά θα το εξηγήσω όσο πιο απλά γίνεται.

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

Ο κατασκευαστής της θυγατρικής κλάσης καλεί τη συνάρτηση super η οποία, ταυτόχρονα, αναφέρεται στη γονική κλάση (BaseGame). Βασικά λέει στην Python:

Συμπληρώστε το χαρακτηριστικό «points_to_win» της γονικής τάξης με 5!

Δεν είναι απαραίτητο να βάλουμε το self, μέσα στο τμήμα super().__init__() μόνο και μόνο επειδή καλούμε το super μέσα στον κατασκευαστή, και αυτό θα είχε ως αποτέλεσμα περιττό.

Χρησιμοποιούμε επίσης τη συνάρτηση super στη μέθοδο εκτέλεσης και θα δούμε τι συμβαίνει σε αυτό το κομμάτι κώδικα.

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

Όπως μπορείτε να παρατηρήσετε τη μέθοδο εκτέλεσης στη γονική κλάση, εκτυπώστε το μήνυμα καλωσορίσματος και περιγραφής. Αλλά είναι καλή ιδέα να διατηρήσετε αυτή τη λειτουργικότητα και επίσης να προσθέσετε επιπλέον στις παιδικές τάξεις. Σύμφωνα με αυτό, χρησιμοποιούμε το super για να εκτελέσουμε όλο τον κώδικα της γονικής μεθόδου πριν εκτελέσουμε το επόμενο κομμάτι.

  Τραβήξτε κινούμενες εικόνες με το iPhone σας και αποθηκεύστε ως GIF

Το άλλο μέρος της λειτουργίας εκτέλεσης είναι αρκετά απλό. Ζητάει από τον χρήστη έναν αριθμό με το μήνυμα της λειτουργίας που πρέπει να απαντήσει. Στη συνέχεια, το αποτέλεσμα συγκρίνεται με τον πραγματικό πολλαπλασιασμό και αν είναι ίσοι, προσθέτει έναν πόντο, αν δεν απογειώνουν 1 ζωή.

Αξίζει να πούμε ότι χρησιμοποιούμε βρόχους while-else. Αυτό υπερβαίνει το εύρος αυτού του άρθρου, αλλά θα δημοσιεύσω ένα σχετικά σε λίγες μέρες.

Τέλος, το get_random_numbers, χρησιμοποιεί τη συνάρτηση random.randint, η οποία επιστρέφει έναν τυχαίο ακέραιο αριθμό εντός του καθορισμένου εύρους. Στη συνέχεια, επιστρέφει μια πλειάδα δύο τυχαίων ακεραίων.

Κλάση τυχαίου πολλαπλασιασμού

Η «δεύτερη λειτουργία», πρέπει να εμφανίζει το παιχνίδι σε μορφή πίνακα πολλαπλασιασμού και να βεβαιωθεί ότι ο χρήστης απαντά σωστά σε τουλάχιστον 2 πίνακες.

Για αυτόν τον σκοπό, θα χρησιμοποιήσουμε ξανά τη δύναμη του super και θα τροποποιήσουμε το χαρακτηριστικό γονικής κλάσης points_to_win σε 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()

Όπως καταλαβαίνετε, τροποποιούμε μόνο τη μέθοδο εκτέλεσης αυτής της κλάσης. Αυτή είναι η μαγεία της κληρονομικότητας, γράφουμε μια φορά τη λογική που χρησιμοποιούμε σε πολλά μέρη και την ξεχνάμε 😅.

Στη μέθοδο εκτέλεσης, χρησιμοποιούμε έναν βρόχο for για να πάρουμε τους αριθμούς από το 1 έως το 10 και να δημιουργήσουμε τη λειτουργία που εμφανίζεται στον χρήστη.

Για άλλη μια φορά, εάν οι ζωές εξαντληθούν ή συμπληρωθούν οι βαθμοί που απαιτούνται για να κερδίσετε, ο βρόχος while θα σπάσει και θα εμφανιστεί το μήνυμα νίκης ή απώλειας.

ΝΑΙ, δημιουργήσαμε τις δύο λειτουργίες του παιχνιδιού, αλλά μέχρι τώρα αν τρέξουμε το πρόγραμμα δεν θα γίνει τίποτα.

Ας ολοκληρώσουμε λοιπόν το πρόγραμμα υλοποιώντας την επιλογή τρόπου λειτουργίας και δημιουργώντας τις κλάσεις ανάλογα με αυτήν την επιλογή.

Εφαρμογή επιλογής

Ο χρήστης θα μπορεί να επιλέξει ποια λειτουργία θέλει να παίξει. Ας δούμε λοιπόν πώς θα το εφαρμόσουμε.

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

Αρχικά, ζητάμε από τον χρήστη να επιλέξει μεταξύ 1 ή 2 λειτουργιών. Εάν η είσοδος δεν είναι έγκυρη, το σενάριο σταματά να εκτελείται. Εάν ο χρήστης επιλέξει την πρώτη λειτουργία, το πρόγραμμα θα εκτελέσει τη λειτουργία παιχνιδιού τυχαίου πολλαπλασιασμού και εάν επιλέξει τη δεύτερη, θα εκτελεστεί η λειτουργία πολλαπλασιασμού πίνακα.

Εδώ είναι πώς θα έμοιαζε.

συμπέρασμα

Συγχαρητήρια, απλά δημιουργήστε μια εφαρμογή Python με Αντικειμενοστραφή Προγραμματισμό.

Όλος ο κώδικας είναι διαθέσιμος στο Αποθετήριο Github.

Σε αυτό το άρθρο μάθατε να:

  • Χρησιμοποιήστε κατασκευαστές κλάσεων Python
  • Δημιουργήστε μια λειτουργική εφαρμογή με το OOP
  • Χρησιμοποιήστε τη συνάρτηση super στις τάξεις Python
  • Εφαρμόστε τις βασικές έννοιες της κληρονομικότητας
  • Εφαρμογή χαρακτηριστικών κλάσης και παρουσίας

Καλή κωδικοποίηση 👨‍💻

Στη συνέχεια, εξερευνήστε μερικά από τα καλύτερα Python IDE για καλύτερη παραγωγικότητα.