Создайте приложение таблицы умножения Python с помощью ООП

Создайте приложение таблицы умножения Python с помощью ООП

В этой статье вы создадите приложение таблицы умножения, используя возможности объектно-ориентированного программирования (ООП) в Python.

Вы будете практиковать основные концепции ООП и способы их использования в полнофункциональном приложении.

Python — мультипарадигмальный язык программирования, а это значит, что мы, как разработчики, можем выбрать наилучший вариант для каждой ситуации и проблемы. Когда мы говорим об объектно-ориентированном программировании, мы имеем в виду одну из наиболее часто используемых парадигм для создания масштабируемых приложений в последние десятилетия.

Основы ООП

Мы кратко рассмотрим самую важную концепцию ООП в Python — классы.

Класс — это шаблон, в котором мы определяем структуру и поведение объектов. Этот шаблон позволяет нам создавать экземпляры, которые представляют собой не что иное, как отдельные объекты, созданные в соответствии с составом класса.

Простой класс книги с атрибутами title и color будет определен следующим образом.

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))
# 
print(type(green_book))
# 

После того, как эти концепции станут кристально чистыми, мы можем приступить к созданию проекта ?.

Заявление о проекте

Работая разработчиками / разработчиками, большую часть времени мы не тратим на написание кода, согласно новостному стеку, мы тратим только треть нашего времени на написание или рефакторинг кода.

Остальные две трети были потрачены на чтение чужого кода и анализ проблемы, над которой мы работали.

Итак, для этого проекта я создам постановку задачи, и мы проанализируем, как построить наше приложение на ее основе. В результате мы создаем законченный процесс, от обдумывания решения до реализации кода.

Классный руководитель хочет поиграть в игру, чтобы проверить навыки умножения 8-10-летних учеников.

В игре должны быть жизни и система очков, где ученик начинает с 3 жизнями и должен набрать определенное количество очков, чтобы выиграть. Программа должна выводить сообщение «проиграл», если студент исчерпал всю свою жизнь.

В игре должно быть два режима, случайное умножение и таблица умножения.

Первый должен дать ученику случайное умножение от 1 до 10, и он должен ответить правильно, чтобы получить балл. Если этого не происходит, ученик проигрывает лайв и игра продолжается. Студент выигрывает только тогда, когда он достигает 5 очков.

Второй режим должен отображать таблицу умножения от 1 до 10, куда ученик должен ввести результат соответствующего умножения. Если учащийся ошибается 3 раза, он проигрывает, но если он заполняет две таблицы, игра заканчивается.

Я знаю, что требования могут быть немного выше, но я обещаю вам, что мы решим их в этой статье ?.

Разделяй и властвуй

Самый важный навык в программировании — решение проблем. Это потому, что у вас должен быть план, прежде чем вы начнете взламывать код.

Я всегда предлагаю взять большую проблему и разбить ее на более мелкие, которые можно решить легко и эффективно.

Так что, если вам нужно создать игру, начните с того, что разбейте ее на самые важные части. Эти подзадачи решить будет гораздо проще.

Именно тогда вы можете иметь ясность в том, как выполнять и интегрировать все с кодом.

Итак, давайте нарисуем график того, как будет выглядеть игра.

Этот рисунок устанавливает отношения между объектами нашего приложения. Как видите, два основных объекта — это «Случайное умножение» и «Табличное умножение». И единственное, что у них общего, это атрибуты Очки и Жизни.

Имея в виду всю эту информацию, давайте приступим к коду.

Создание игрового класса Parent

При работе с объектно-ориентированным программированием мы ищем самый чистый способ избежать повторения кода. Это называется внедорожник (не повторяйтесь).

Примечание. Эта цель связана не с написанием меньшего количества строк кода (качество кода не должно измеряться этим аспектом), а с абстрагированием наиболее часто используемой логики.

Согласно предыдущей идее, родительский класс нашего приложения должен установить структуру и желаемое поведение двух других классов.

Посмотрим, как это будет сделано.

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

Вау, похоже, это довольно большой класс. Позвольте мне объяснить это подробно.

Прежде всего, давайте разберемся с атрибутами класса и конструктором.

По сути, атрибуты класса — это переменные, созданные внутри класса, но вне конструктора или любого метода.

Хотя атрибуты — это переменные экземпляра, созданные только внутри конструктора.

Основное различие между ними заключается в масштабе. то есть Атрибуты класса доступны как из объекта экземпляра, так и из класса. С другой стороны, атрибуты экземпляра доступны только из объекта экземпляра.

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 используется для предотвращения ввода пользователем любых нечисловых данных. Как видите, этот метод предназначен для подсказок пользователю до тех пор, пока он не введет числовой ввод. Мы будем использовать его позже на уроках ребенка.

Методы печати позволяют нам сохранять повторяющуюся печать одного и того же каждый раз, когда в игре происходит событие.

И последнее, но не менее важное: метод run — это всего лишь оболочка, которую классы Random Multiply и Table Multiply будут использовать для взаимодействия с пользователем и обеспечения функциональности.

Создание дочерних классов

После того, как мы создали этот родительский класс, который устанавливает структуру и некоторые функции нашего приложения, пришло время создать настоящие классы игрового режима, используя силу наследования.

Класс случайного умножения

Этот класс будет запускать «первый режим» нашей игры. Разумеется, будет использоваться модуль random, который даст нам возможность задавать пользователю случайные операции от 1 до 10. Вот отличная статья про random (и другие важные модули) ?.

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)

Конструктор дочернего класса вызывает суперфункцию, которая одновременно обращается к родительскому (BaseGame) классу. Это в основном говорит Python:

Заполните атрибут «points_to_vin» родительского класса на 5!

Нет необходимости помещать self внутрь части super().__init__() только потому, что мы вызываем super внутри конструктора, и это приведет к избыточности.

Мы также используем функцию super в методе run и смотрим, что происходит в этой части кода.

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

Как вы можете заметить, метод run в родительском классе выводит приветственное сообщение и описание. Но было бы неплохо сохранить эту функциональность и добавить дополнительные возможности в дочерние классы. Поэтому мы используем super для запуска всего кода в родительском методе перед запуском следующей части.

Вторая часть функции запуска довольно проста. Он запрашивает у пользователя номер с сообщением об операции, на которое он должен ответить. Затем результат сравнивается с реальным умножением и если они равны, добавляется очко, если нет отнимают 1 жизнь.

Стоит отметить, что мы используем циклы while-else. Это выходит за рамки этой статьи, но я напишу об этом через несколько дней.

Наконец, get_random_numbers использует функцию random.randint, которая возвращает случайное целое число в указанном диапазоне. Затем он возвращает набор из двух случайных целых чисел.

Класс случайного умножения

«Второй режим» должен отображать игру в формате таблицы умножения и убедиться, что пользователь правильно ответил как минимум на 2 таблицы.

Для этой цели мы снова воспользуемся силой super и изменим атрибут point_to_vin родительского класса на 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 = self.points_to_win:
                self.print_win_message()
            else:
                self.print_lose_message()

Как вы понимаете, мы меняем только метод инициализации этого класса. В этом и заключается магия наследования: стоит нам записать логику, которую мы используем в нескольких местах, и забыть о ней ?.

В методе run мы используем цикл 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
  • Создайте функциональное приложение с ООП
  • Используйте суперфункцию в классах Python
  • Применение основных концепций наследования
  • Реализовать атрибуты класса и экземпляра

Удачного кодирования ?‍?

Затем изучите некоторые из лучших IDE Python для повышения производительности.

Поделиться в соцсетях