Sestavte aplikaci Python Multiplication Table s OOP

V tomto článku se chystáte vytvořit aplikaci pro násobící tabulky s využitím výkonu objektově orientovaného programování (OOP) v Pythonu.

Procvičíte si hlavní koncepty OOP a jak je používat v plně funkční aplikaci.

Python je multiparadigmatický programovací jazyk, což znamená, že my jako vývojáři si můžeme vybrat nejlepší možnost pro každou situaci a problém. Když mluvíme o objektově orientovaném programování, máme na mysli jedno z nejpoužívanějších paradigmat pro vytváření škálovatelných aplikací v posledních desetiletích.

Základy OOP

Pojďme se rychle podívat na nejdůležitější koncept OOP v Pythonu, třídy.

Třída je šablona, ​​ve které definujeme strukturu a chování objektů. Tato šablona nám umožňuje vytvářet instance, což nejsou nic jiného než jednotlivé objekty vytvořené podle složení třídy.

Jednoduchá třída knihy s atributy názvu a barvy by byla definována následovně.

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

Pokud chceme vytvořit instance třídní knihy, musíme třídu zavolat a předat jí argumenty.

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

Dobrá reprezentace našeho současného programu by byla:

Úžasná věc je, že když zkontrolujeme typ instancí blue_book a green_book, dostaneme „Book“.

# Printing the type of the books

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

Poté, co máme tyto koncepty křišťálově jasné, můžeme začít budovat projekt 😃.

Prohlášení projektu

Zatímco pracujete jako vývojáři/programátoři, většinu času nestrávíte psaním kódu newstack psaním nebo refaktorováním kódu strávíme pouze třetinu našeho času.

Další dvě třetiny jsme strávili čtením cizího kódu a analýzou problému, na kterém pracujeme.

Takže pro tento projekt vygeneruji prohlášení o problému a budeme analyzovat, jak z něj vytvořit naši aplikaci. Výsledkem je, že děláme celý proces, od přemýšlení o řešení až po jeho aplikaci s kódem.

Primární učitel chce hru, která by otestovala dovednosti násobení žáků od 8 do 10 let.

Hra musí mít životy a bodový systém, kdy žák začíná se 3 životy a pro výhru musí dosáhnout určitého počtu bodů. Pokud student vyčerpá celý svůj život, musí program zobrazit zprávu „ztratit“.

Hra musí mít dva režimy, náhodné násobení a tabulkové násobení.

První by měl dát studentovi náhodné vynásobení od 1 do 10 a on/ona musí správně odpovědět, aby získal bod. Pokud se tak nestane, student ztratí život a hra pokračuje. Student vyhraje pouze tehdy, když dosáhne 5 bodů.

Druhý režim musí zobrazit tabulku násobení od 1 do 10, kde student musí zadat výsledek příslušného násobení. Pokud student 3x neuspěje, prohrává, ale pokud dokončí dva stoly, hra končí.

Vím, že požadavky jsou možná trochu větší, ale slibuji vám, že je vyřešíme v tomto článku 😁.

Rozděl a panuj

Nejdůležitější dovedností v programování je řešení problémů. Je to proto, že musíte mít plán, než začnete kód hackovat.

Vždy navrhuji vzít větší problém a rozdělit ho na menší, které lze snadno a efektivně vyřešit.

  Spravujte své obchodní úkoly efektivně s Monday.com

Pokud tedy potřebujete vytvořit hru, začněte tím, že ji rozdělíte na její nejdůležitější části. Tyto dílčí problémy bude mnohem snazší vyřešit.

Právě pak můžete mít jasno v tom, jak vše spustit a integrovat do kódu.

Pojďme si tedy udělat graf, jak by hra vypadala.

Tato grafika určuje vztahy mezi objekty naší aplikace. Jak můžete vidět, dva hlavní objekty jsou náhodné násobení a tabulkové násobení. A jediné, co sdílejí, jsou atributy Body a Životy.

S ohledem na všechny tyto informace se pustíme do kódu.

Vytvoření třídy hry Parent

Když pracujeme s objektově orientovaným programováním, hledáme nejčistší způsob, jak se vyhnout opakování kódu. Tomu se říká SUCHÝ (neopakuj se).

Poznámka: Tento cíl nesouvisí s psaním menšího počtu řádků kódu (kvalita kódu se nesmí měřit tímto aspektem), ale s abstrahováním nejpoužívanější logiky.

Podle předchozí myšlenky musí nadřazená třída naší aplikace vytvořit strukturu a požadované chování ostatních dvou tříd.

Podívejme se, jak by se to udělalo.

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

Páni, to vypadá na docela velkou třídu. Dovolte mi to vysvětlit do hloubky.

Nejprve si ujasněme atributy třídy a konstruktor.

Atributy třídy jsou v zásadě proměnné vytvořené uvnitř třídy, ale mimo konstruktor nebo jakoukoli metodu.

Zatímco atributy instance jsou proměnné vytvořené pouze uvnitř konstruktoru.

Hlavním rozdílem mezi těmito dvěma je rozsah. tj. atributy třídy jsou přístupné jak z objektu instance, tak z třídy. Na druhou stranu atributy instance jsou přístupné pouze z objektu 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

Další článek se může ponořit hlouběji do tohoto tématu. Zůstaňte v kontaktu a přečtěte si to.

Funkce get_numeric_input se používá k tomu, aby zabránila uživateli poskytnout jakýkoli vstup, který není číselný. Jak si můžete všimnout, tato metoda je navržena tak, aby se uživatele zeptala, dokud nedostane číselný vstup. Využijeme to později v hodinách dítěte.

  Jak generovat protokoly Microsoft Teams pro odstraňování problémů

Metody tisku nám umožňují uložit opakování tisku stejné věci pokaždé, když ve hře dojde k události.

V neposlední řadě je metoda run jen obal, který třídy Náhodné násobení a násobení tabulky použijí k interakci s uživatelem a zfunkčnění všeho.

Vytváření dětských tříd

Jakmile vytvoříme nadřazenou třídu, která zavádí strukturu a některé funkce naší aplikace, je čas vytvořit skutečné třídy herních režimů s využitím síly dědičnosti.

Třída náhodného násobení

Tato třída spustí „první režim“ naší hry. Bude samozřejmě používat modul random, který nám dá možnost ptát se uživatele na náhodné operace od 1 do 10. Zde je výborný článek o random (a dalších důležitých modulech) 😉.

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

Je tu další masivní třída 😅. Ale jak jsem uvedl dříve, nejde o počet řádků, ale o to, jak je to čitelné a efektivní. A nejlepší na Pythonu je, že umožňuje vývojářům vytvářet čistý a čitelný kód, jako by mluvili normální angličtinou.

Tato třída má jednu věc, která vás může zmást, ale vysvětlím to co nejjednodušeji.

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

Konstruktor podřízené třídy volá funkci super, která zároveň odkazuje na nadřazenou třídu (BaseGame). V podstatě říká Pythonu:

Vyplňte atribut „points_to_win“ nadřazené třídy 5!

Není nutné vkládat self do části super().__init__() jen proto, že voláme super uvnitř konstruktoru, což by vedlo k nadbytečnosti.

Také používáme super funkci v metodě run a uvidíme, co se děje v tomto kusu kódu.

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

Jak si můžete všimnout metody run v nadřazené třídě, vytiskněte uvítací a popisnou zprávu. Je však dobré tuto funkci ponechat a také přidat další funkce do dětských tříd. Podle toho používáme super ke spuštění celého kódu rodičovské metody před spuštěním dalšího kusu.

Další část funkce run je docela jednoduchá. Požaduje od uživatele číslo se zprávou operace, na kterou musí odpovědět. Poté se výsledek porovná se skutečným násobením a pokud se rovnají, přidá bod, pokud neuberou 1 život.

  Jak smazat účet Square

Stojí za to říci, že používáme smyčky while-else. To přesahuje rozsah tohoto článku, ale jeden o tom zveřejním za několik dní.

Nakonec get_random_numbers používá funkci random.randint, která vrací náhodné celé číslo v určeném rozsahu. Potom vrátí n-tici dvou náhodných celých čísel.

Třída náhodného násobení

„Druhý režim“ musí zobrazovat hru ve formátu násobilky a zajistit, aby uživatel správně odpověděl alespoň na 2 stoly.

Za tímto účelem opět využijeme sílu super a upravíme atribut rodičovské třídy points_to_win na 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()

Jak si můžete uvědomit, pouze upravujeme metodu běhu této třídy. To je kouzlo dědičnosti, jednou napíšeme logiku, kterou používáme na více místech, a zapomeneme na to 😅.

V metodě run používáme cyklus for k získání čísel od 1 do 10 a sestavíme operaci, která je zobrazena uživateli.

Opět, pokud jsou životy vyčerpány nebo jsou dosaženy body potřebné k vítězství, smyčka while se přeruší a zobrazí se zpráva o výhře nebo prohře.

ANO, vytvořili jsme dva režimy hry, ale pokud program spustíme, nic se nestane.

Pojďme tedy dokončit program implementací volby režimu a vytvořením instance tříd v závislosti na této volbě.

Implementace volby

Uživatel si bude moci vybrat, jaký režim chce hrát. Pojďme se tedy podívat, jak to implementovat.

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

Nejprve požádáme uživatele, aby si vybral mezi 1 nebo 2 režimy. Pokud vstup není platný, skript se zastaví. Pokud uživatel zvolí první režim, program spustí herní režim Náhodné násobení, a pokud zvolí druhý režim, spustí se režim násobení tabulky.

Zde je návod, jak by to vypadalo.

Závěr

Gratuluji, ty jen vytvořit aplikaci Python s objektově orientovaným programováním.

Veškerý kód je k dispozici v úložiště Github.

V tomto článku jste se naučili:

  • Použijte konstruktory třídy Python
  • Vytvořte funkční aplikaci s OOP
  • Použijte super funkci v třídách Pythonu
  • Aplikujte základní pojmy dědičnosti
  • Implementujte atributy třídy a instance

Veselé kódování 👨‍💻

Dále prozkoumejte některé z nejlepších Python IDE pro lepší produktivitu.