Creați o aplicație Python pentru tabel de înmulțire cu POO

În acest articol, veți construi o aplicație pentru tabele de înmulțire, folosind puterea programării orientate pe obiecte (OOP) în Python.

Veți exersa conceptele principale ale OOP și cum să le utilizați într-o aplicație complet funcțională.

Python este un limbaj de programare multiparadigmă, ceea ce înseamnă că noi, ca dezvoltatori, putem alege cea mai bună opțiune pentru fiecare situație și problemă. Când vorbim de Programare Orientată pe Obiect, ne referim la una dintre cele mai utilizate paradigme pentru a construi aplicații scalabile, în ultimele decenii.

Elementele de bază ale OOP

Vom arunca o privire rapidă asupra celui mai important concept de POO în Python, clasele.

O clasă este un șablon în care definim structura și comportamentul obiectelor. Acel șablon ne permite să creăm Instanțe, care nu sunt altceva decât obiecte individuale realizate în urma compoziției clasei.

O clasă simplă de carte, cu atributele titlului și culorii, ar fi definită după cum urmează.

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

Dacă vrem să creăm instanțe ale cărții de clasă, trebuie să apelăm clasa și să îi transmitem argumente.

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

O bună reprezentare a programului nostru actual ar fi:

Lucrul minunat este că atunci când verificăm tipul instanțelor blue_book și green_book, obținem „Carte”.

# Printing the type of the books

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

După ce avem aceste concepte clare, putem începe să construim proiectul 😃.

Declarație de proiect

În timp ce lucrează ca dezvoltatori/programatori, cea mai mare parte a timpului nu este petrecut scriind cod, potrivit thenewsstack ne petrecem doar o treime din timp scriind sau refactoring cod.

Celelalte două treimi le-am petrecut citind codul altora și analizând problema la care lucrăm.

Deci, pentru acest proiect, voi genera o declarație de problemă și vom analiza cum să ne creăm aplicația din ea. Ca urmare, facem procesul complet, de la gândirea la soluție pentru a o aplica cu cod.

Un profesor de primar vrea un joc care să testeze abilitățile de multiplicare ale elevilor de la 8 la 10 ani.

Jocul trebuie să aibă un sistem de vieți și un sistem de puncte, în care elevul începe cu 3 vieți și trebuie să atingă o anumită cantitate de puncte pentru a câștiga. Programul trebuie să arate un mesaj „pierde” dacă studentul își epuizează toată viața.

Jocul trebuie să aibă două moduri, înmulțiri aleatorii și înmulțiri la masă.

Primul ar trebui să ofere elevului o înmulțire aleatorie de la 1 la 10, iar el/ea trebuie să răspundă corect pentru a câștiga un punct. Dacă acest lucru nu se întâmplă, studentul pierde un live și jocul continuă. Elevul câștigă doar când atinge 5 puncte.

Al doilea mod trebuie să afișeze o tabelă de înmulțire de la 1 la 10, unde elevul trebuie să introducă rezultatul înmulțirii respective. Dacă elevul eșuează de 3 ori, el/ea pierde, dar dacă completează două mese, jocul se termină.

Știu că cerințele poate un pic mai mari, dar vă promit că le vom rezolva în acest articol 😁.

Diviza și cuceri

Cea mai importantă abilitate în programare este rezolvarea problemelor. Acest lucru se datorează faptului că trebuie să aveți un plan înainte de a începe să piratați codul.

  Cum să configurați jocurile 4K pe Xbox Series X

Întotdeauna sugerez să luați problema mai mare și să o împărțiți în altele mai mici, care pot fi ambele, ușor și eficient rezolvate.

Deci, dacă trebuie să creați un joc, începeți prin a-l împărți în cele mai importante părți ale acestuia. Aceste sub-probleme vor fi mult mai ușor de rezolvat.

Tocmai atunci puteți avea claritatea cum să executați și să integrați totul cu codul.

Deci haideți să facem un grafic despre cum ar arăta jocul.

Acest grafic stabilește relațiile dintre obiectele aplicației noastre. După cum puteți vedea, cele două obiecte principale sunt înmulțirea aleatorie și înmulțirea tabelului. Și singurul lucru pe care îl împărtășesc sunt atributele Puncte și Vieți.

Având în vedere toate aceste informații, să intrăm în cod.

Crearea clasei de joc pentru părinți

Când lucrăm cu programare orientată pe obiecte, căutăm cea mai curată modalitate de a evita repetarea codului. Aceasta se numește USCAT (nu te repeta).

Notă: Acest obiectiv nu este legat de scrierea a mai puține linii de cod (calitatea codului nu trebuie măsurată după acest aspect) ci de a abstractiza logica cea mai folosită.

Conform ideii anterioare, clasa părinte a aplicației noastre trebuie să stabilească structura și comportamentul dorit al celorlalte două clase.

Să vedem cum s-ar face.

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

Uau, aceasta pare o clasă destul de mare. Lasă-mă să explic în profunzime.

În primul rând, să înțelegem atributele clasei și constructorul.

Practic, atributele clasei, sunt variabile create în interiorul clasei, dar în afara constructorului sau a oricărei metode.

În timp ce atributele instanței sunt variabile create numai în interiorul constructorului.

Principala diferență între acestea două este domeniul de aplicare. adică atributele de clasă sunt accesibile atât dintr-un obiect instanță, cât și din clasă. Pe de altă parte, atributele instanței sunt accesibile doar dintr-un obiect instanță.

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

Un alt articol poate aprofunda acest subiect. Rămâneți în legătură pentru a o citi.

Funcția get_numeric_input este utilizată pentru a împiedica utilizatorul să furnizeze orice intrare care nu este numerică. După cum puteți observa, această metodă este concepută pentru a cere utilizatorului până când primește o intrare numerică. Il vom folosi mai tarziu in cursurile copilului.

  Ce este Zoombombing și cum îl poți opri?

Metodele de printare ne permit să salvăm repetarea tipăririi aceluiași lucru de fiecare dată când apare un eveniment în joc.

Nu în ultimul rând, metoda run este doar un înveliș pe care clasele de înmulțire aleatorie și de înmulțire pe tabel îl vor folosi pentru a interacționa cu utilizatorul și a face totul funcțional.

Crearea claselor copilului

Odată ce am creat acea clasă părinte, care stabilește structura și unele dintre funcționalitățile aplicației noastre, este timpul să construim clasele reale ale modului de joc, folosind puterea moștenirii.

Clasa de multiplicare aleatorie

Această clasă va rula „primul mod” al jocului nostru. Desigur, va folosi modulul aleatoriu, care ne va oferi posibilitatea de a cere utilizatorului operațiuni aleatorii de la 1 la 10. Iată un articol excelent despre module aleatoare (și alte module importante) 😉.

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

Iată o altă clasă masivă 😅. Dar, așa cum am spus mai devreme, nu este vorba de numărul de linii necesare, ci de cât de mult lizibil și eficient este. Și cel mai bun lucru despre Python este că le permite dezvoltatorilor să creeze cod curat și ușor de citit ca și cum ar vorbi o engleză normală.

Această clasă are un lucru care te poate deruta, dar o voi explica cât mai simplu posibil.

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

Constructorul clasei copil apelează superfuncția care, în același timp, se referă la clasa părinte (BaseGame). Practic îi spune lui Python:

Completați atributul „points_to_win” al clasei părinte cu 5!

Nu este necesar să punem self, în interiorul părții super().__init__() doar pentru că numim super în interiorul constructorului și ar rezulta redundant.

De asemenea, folosim funcția super în metoda run și vom vedea ce se întâmplă în acea bucată de cod.

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

După cum puteți observa metoda de rulare în clasa părinte, imprimați mesajul de bun venit și descriere. Dar este o idee bună să păstrați această funcționalitate și, de asemenea, să adăugați altele suplimentare în clasele pentru copii. În conformitate cu asta, folosim super pentru a rula tot codul metodei părinte înainte de a rula piesa următoare.

  Dezlănțuirea puterii fotografiei captivante

Cealaltă parte a funcției de rulare este destul de simplă. Îi cere utilizatorului un număr cu mesajul operației la care trebuie să răspundă. Apoi rezultatul este comparat cu înmulțirea reală și dacă sunt egale, se adaugă un punct, dacă nu scot 1 viață.

Merită să spunem că folosim bucle while-else. Acest lucru depășește scopul acestui articol, dar voi publica unul despre el în câteva zile.

În cele din urmă, get_random_numbers, folosește funcția random.randint, care returnează un număr întreg aleator în intervalul specificat. Apoi returnează un tuplu de două numere întregi aleatorii.

Clasa de multiplicare aleatorie

„Al doilea mod”, trebuie să afișeze jocul într-un format de masă de înmulțire și să se asigure că utilizatorul răspunde corect la cel puțin 2 mese.

În acest scop, vom folosi din nou puterea super și vom modifica atributul clasei părinte points_to_win la 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()

După cum vă puteți da seama, modificăm doar metoda de rulare a acestei clase. Asta e magia moștenirii, scriem o dată logica pe care o folosim în mai multe locuri și uităm de ea 😅.

În metoda run, folosim o buclă for pentru a obține numerele de la 1 la 10 și construim operația care este afișată utilizatorului.

Încă o dată, dacă viețile sunt epuizate sau punctele necesare pentru a câștiga sunt atinse, bucla while se va întrerupe și va fi afișat mesajul de câștig sau pierdere.

DA, am creat cele două moduri ale jocului, dar până acum dacă rulăm programul nu se va întâmpla nimic.

Deci, să finalizăm programul prin implementarea modului de alegere și instanțiarea claselor în funcție de acea alegere.

Implementarea alegerii

Utilizatorul va putea alege ce mod vrea să joace. Deci, să vedem cum să o implementăm.

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

În primul rând, îi cerem utilizatorului să aleagă între 1 sau 2 moduri. Dacă intrarea nu este validă, scriptul se oprește. Dacă utilizatorul selectează primul mod, programul va rula modul de joc Random Multiplication, iar dacă el/ea selectează al doilea, va fi rulat modul de înmulțire Tabel.

Iată cum ar arăta.

Concluzie

Felicitări, doar tu construiți o aplicație Python cu programare orientată pe obiecte.

Tot codul este disponibil în Depozitul Github.

În acest articol ați învățat să:

  • Utilizați constructorii clasei Python
  • Creați o aplicație funcțională cu OOP
  • Utilizați funcția super în clasele Python
  • Aplicați conceptele de bază ale moștenirii
  • Implementați atributele de clasă și instanță

Codare fericită 👨‍💻

Apoi, explorați unele dintre cele mai bune IDE Python pentru o productivitate mai bună.