使用 OOP 构建 Python 乘法表应用程序

在本文中,您将使用 Python 中面向对象编程 (OOP) 的强大功能来构建一个乘法表应用程序。

您将练习 OOP 的主要概念,以及如何在功能齐全的应用程序中使用它们。

Python 是一种多范式编程语言,这意味着我们作为开发人员可以针对每种情况和问题选择最佳方案。 当我们谈论面向对象编程时,我们指的是过去几十年中最常用的构建可扩展应用程序的范例之一。

面向对象的基础知识

我们将快速浏览一下 Python 中最重要的 OOP 概念,即类。

类是一个模板,我们在其中定义对象的结构和行为。 该模板允许我们创建实例,这些实例只不过是按照类的组合而创建的单个对象。

一个简单的书籍类,具有标题和颜色属性,定义如下。

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

如果我们想创建类 book 的实例,我们必须调用类并向它传递参数。

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

清楚这些概念后,我们就可以开始构建项目了😃。

项目声明

据称,作为开发人员/程序员,大部分时间都不会花在编写代码上 新堆栈 我们只花三分之一的时间编写或重构代码。

我们用另外三分之二的时间阅读其他人的代码并分析我们正在处理的问题。

因此,对于这个项目,我将生成一个问题陈述,我们将分析如何从中创建我们的应用程序。 因此,我们正在制定完整的流程,从思考解决方案到用代码应用它。

一位小学老师想要一款游戏来测试 8 到 10 岁学生的乘法技能。

游戏必须有生命和分数系统,学生从 3 条生命开始,必须达到一定数量的分数才能获胜。 如果学生耗尽了他/她的所有生命,程序必须显示“失败”信息。

游戏必须有两种模式,随机乘法和表乘法。

第一个应该给学生一个从 1 到 10 的随机乘法,他/她必须回答正确才能赢得一分。 如果这没有发生,则学生输掉一场比赛,比赛继续进行。 学生只有在她/他达到 5 分时才获胜。

第二种模式必须显示从 1 到 10 的乘法表,学生必须在其中输入相应乘法的结果。 如果学生失败 3 次,他/她就输了,但如果她/他完成了两张桌子,则游戏结束。

我知道要求可能有点大,但我向你保证,我们将在本文中解决它们😁。

分而治之

编程中最重要的技能是解决问题。 这是因为在开始破解代码之前,您需要有一个计划。

我总是建议把更大的问题分解成更小的问题,这样既可以简单又有效地解决。

因此,如果您需要创建游戏,请先将其分解为最重要的部分。 这些子问题将更容易解决。

就在那时,您可以清楚地了解如何执行所有内容并将其与代码集成。

因此,让我们绘制一张游戏的外观图。

此图形建立了我们应用程序对象之间的关系。 如您所见,两个主要对象是随机乘法和表乘法。 他们唯一共享的是点数和生命值属性。

记住所有这些信息后,让我们进入代码。

创建父游戏类

当我们使用面向对象编程时,我们会寻找最简洁的方法来避免代码重复。 这就是所谓的 干燥 (不要重复自己)。

注意:这个目标与编写更少的代码行无关(代码质量不能用那个方面来衡量),而是抽象出最常用的逻辑。

根据之前的想法,我们应用程序的父类必须建立其他两个类的结构和期望的行为。

让我们看看它是如何完成的。

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

哇,这似乎是一个很大的课程。 让我深入解释一下。

  总结你一天的 14 个最佳日记应用程序

首先,让我们了解类属性和构造函数。

基本上,类属性是在类内部创建的变量,但在构造函数或任何方法之外。

而实例属性是仅在构造函数内部创建的变量。

这两者之间的主要区别是范围。 即类属性可以从实例对象和类中访问。 另一方面,实例属性只能从实例对象访问。

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 方法只是一个包装器,随机乘法和表乘法类将使用它来与用户交互并使一切正常运行。

创建孩子的班级

一旦我们创建了父类,它建立了我们应用程序的结构和一些功能,就可以使用继承的力量构建实际的游戏模式类了。

随机乘法类

这个类将运行我们游戏的“第一模式”。 它当然会使用随机模块,这将使我们能够要求用户从 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 最好的一点是,它允许开发人员编写出清晰易读的代码,就像他们在说普通英语一样。

  如何在 PowerPoint 中模糊图像

这门课有一件事可能会让你感到困惑,但我会尽可能简单地解释它。

    # 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_win”属性为5!

没有必要仅仅因为我们在构造函数中调用 super 而将 self, 放在 super().__init__() 部分中,这会导致冗余。

我们还在 run 方法中使用了 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()
        
        .....

您可能会注意到父类中的 run 方法,打印欢迎和描述消息。 但保留该功能并在子类中添加额外的功能是个好主意。 据此,我们使用 super 在运行下一段之前运行父方法的所有代码。

run 函数的另一部分非常简单。 它要求用户提供一个数字,其中包含他/她必须响应的操作消息。 然后将结果与实际乘法进行比较,如果相等,则加一分,如果它们不减 1 条生命。

值得一提的是,我们正在使用 while-else 循环。 这超出了本文的范围,但我将在几天后发布一篇关于它的文章。

最后,get_random_numbers,使用函数random.randint,返回指定范围内的随机整数。 然后它返回两个随机整数的元组。

随机乘法类

“第二种模式”,必须以九九九乘法表的形式展示游戏,并确保用户至少答对两张表。

为此,我们将再次使用 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()

如您所知,我们只是在修改此类的运行方法。 这就是继承的魔力,我们一次编写我们在多个地方使用的逻辑,然后就把它忘掉了😀。

  团队合作软件让团队管理变得简单

在 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 类构造函数
  • 使用 OOP 创建功能性应用程序
  • 在 Python 类中使用 super 函数
  • 应用继承的基本概念
  • 实现类和实例属性

快乐编码👨‍💻

接下来,探索一些最好的 Python IDE 以提高工作效率。