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.
Table of Contents
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.
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.
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.
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.