使用面向对象编程构建一个Python乘法表应用程序

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

您将练习O.O.P的主要概念以及如何在完全功能的应用程序中使用它们。

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

OOP的基本知识

我们将快速了解O.O.P在Python中最重要的概念,即类。

是一个模板,我们可以在其中定义对象的结构和行为。该模板允许我们创建实例,这些实例只是按照类的组成方式制作的各个对象。

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

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

如果我们想要创建类书的实例,我们必须调用类并向其传递参数

# Book类的实例对象
blue_book = Book("The blue kid", "Blue")
green_book = Book("The frog story", "Green")

我们当前程序的良好表示如下:

太棒了,当我们检查blue_bookgreen_book实例的类型时,我们得到的是“Book”。

# 打印书籍的类型

print(type(blue_book))
# 
print(type(green_book))
# 

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

项目陈述

当我们作为开发人员/程序员工作时,大部分时间都不是用来编写代码的,根据thenewstack,我们只有三分之一的时间用于编写或重构代码。

我们花费的另外三分之二的时间是阅读其他人的代码和analyzing the problem我们正在处理的内容。

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

一位小学教师需要一个测试8至10岁学生乘法技能的游戏。

游戏必须有一个生命和得分系统,学生从3个生命开始,必须达到一定的得分才能获胜。如果学生耗尽所有生命,程序必须显示“输”信息。

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

第一种模式应该给学生一个从1到10的随机乘法,他/她必须正确回答才能获得一分。如果这种情况不发生,学生就会失去一条生命并继续游戏。只有当学生达到5分时,他/她才能获胜。

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

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

分而治之

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

我总是建议将较大的问题分解为可以轻松有效地解决的更小的问题。

所以,如果你需要 create a game,首先要分解成它最重要的部分。这些子问题会更容易解决。

然后你就能清楚地知道如何执行和将所有内容与代码集成。

所以让我们画出游戏的样子。

这张图建立了我们应用程序中对象之间的关系。你可以看到两个主要对象是随机乘法表格乘法。它们唯一共享的是属性分数生命

有了所有这些信息,让我们进入代码。

创建父游戏类

当我们使用面向对象的编程时,我们寻找避免代码重复的最简洁方法。这被称为 DRY(不要重复自己)。

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

根据上述思想,我们应用程序的父类必须建立其他两个类的结构和所需行为。

让我们看看如何实现。

class BaseGame:

    # 信息居中的长度
    message_lenght = 60
    
    description = ""    
        
    def __init__(self, points_to_win, n_lives=3):
        """Base game class

        Args:
            points_to_win (int): 游戏需要达到的分数
            n_lives (int): 学生拥有的生命数。默认值为3。
        """
        self.points_to_win = points_to_win

        self.points = 0
        
        self.lives = n_lives

    def get_numeric_input(self, message=""):

        while True:
            # 获取用户输入
            user_input = input(message) 
            
            # 如果输入是数字,则返回它
            # 如果不是,则打印一条消息并重复
            if user_input.isnumeric():
                return int(user_input)
            else:
                print("输入必须是一个数字")
                continue     
             
    def print_welcome_message(self):
        print("PYTHON乘法游戏".center(self.message_lenght))

    def print_lose_message(self):
        print("抱歉,你失去了所有生命".center(self.message_lenght))

    def print_win_message(self):
        print(f"恭喜你达到了{self.points}分".center(self.message_lenght))
        
    def print_current_lives(self):
        print(f"当前你有{self.lives}条生命n")

    def print_current_score(self):
        print(f"n你的得分是{self.points}")

    def print_description(self):
        print("nn" + self.description.center(self.message_lenght) + "n")

    # 基本运行方法
    def run(self):
        self.print_welcome_message()
        
        self.print_description()

哇,这个类看起来相当庞大。让我深入解释一下。

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

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

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

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

游戏 = BaseGame(5)

# 从类中访问游戏消息长度类属性
print(游戏.message_lenght) # 60

# 从类中访问消息长度类属性
print(BaseGame.message_lenght) # 60

# 从实例中访问points实例属性
print(游戏.points) # 0

# 从类中访问points实例属性
print(BaseGame.points) # 属性错误

另一篇文章可以更深入地探讨这个主题。请保持联系以阅读它。

get_numeric_input函数用于防止用户提供非数值输入。正如您可能注意到的,此方法被设计为要求用户直到它获取数值输入为止。我们将在子类中稍后使用它。

print方法允许我们保存在游戏中每次事件发生时都打印相同内容的重复。

最后但并非最不重要的,run方法只是一个包装器,随机乘法和表乘法类将使用它与用户交互并使一切功能正常。

创建子类

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

随机乘法类

这个类将运行我们游戏的“第一模式”。当然,它将使用random模块,这将使我们能够向用户询问1到10的随机运算。链接7是关于随机(和其他重要模块)的优秀文章。

import random #用于随机操作的模块

类随机乘法(BaseGame):

描述=“在这个游戏中,您必须正确回答随机乘法n如果您达到5分,您将获胜,如果您失去所有生命,您将失败”

def __init__(self):
#需要获胜的分数是5
#传递5个“points_to_win”参数
super()。__init__(5)

def get_random_numbers(self):

第一个数字= random.randint(1, 10)
第二个数字= random.randint(1, 10)

return first_number,second_number

def run(self):

#调用上层类打印欢迎消息
super()。run()

while self.lives > 0 and self.points_to_win > self.points:
#获取两个随机数字
number1,number2 = self.get_random_numbers()

operation = f“{number1} x {number2}:”

#要求用户回答该操作
#防止值错误
user_answer = self.get_numeric_input(message=operation)

if user_answer == number1 * number2:
print(“n您的答案是正确的n”)

#增加一个点
self.points += 1
else:
print(“n对不起,您的答案是错误的n”)

#减去一个生命
self.lives -= 1

self.print_current_score()
self.print_current_lives()

#仅在游戏完成时执行
#并且没有一个条件为真
else:
#打印最终消息

if self.points >= self.points_to_win:
self.print_win_message()
else:
self.print_lose_message()

这是另一个庞大的类😅。但正如我之前所说,它不是所需的行数,而是可读性和效率。而Python最好的地方在于它允许开发人员编写干净易读的代码,就像他们在用正常英语交谈一样。

这个类有一件事可能会让你困惑,但我会尽可能简单地解释。

    # 父类
    def __init__(self, points_to_win, n_lives=3):
        "...
    # 子类
    def __init__(self):
        # 赢得比赛所需的点数是5
        # 传递5个 "points_to_win" 参数
        super().__init__(5)

子类的构造函数调用了super函数,同时引用了父类(BaseGame)的类。它基本上告诉Python:

用5来填充父类的“points_to_win”属性!

self内部不必要放置self,因为我们在构造函数内调用了super,这将导致冗余。

我们还在run方法中使用了super函数,我们将看到在那段代码中发生了什么。

    # 基本的run方法
    # 父类方法
    def run(self):
        self.print_welcome_message()
        
        self.print_description()
    def run(self):
        
        # 调用上一层类来打印欢迎消息
        super().run()
        
        .....

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

run函数的另一部分非常直接。它要求用户输入一个数字,并显示操作的消息。然后将结果与实际乘法进行比较,如果它们相等,则添加一个点,如果不相等,则减去一个生命。

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

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

随机乘法类

“第二种模式”必须以乘法表格式显示游戏,并确保用户至少正确回答2个表格。

为此,我们将再次使用super的功能,并修改父类属性points_to_win为2。

class TableMultiplication(BaseGame):

description = “在这个游戏中,您必须正确解决完整的乘法表n如果您解决了2个乘法表,您将获胜”

def __init__(self):
# 需要完成2个乘法表才能获胜
super().__init__(2)

def run(self):

# 打印欢迎信息
super().run()

while self.lives > 0 and self.points_to_win > self.points:
# 获取两个随机数
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方法。这就是继承的魔力所在,我们在多个地方写一次我们使用的逻辑,然后忘记它😅。

# 在run方法中,我们使用了一个循环来获取从1到10的数字,并构建显示给用户的操作。

# 再一次,如果生命用尽或达到了所需的胜利点数,while循环将中断,并显示胜利或失败消息。

# 是的,我们创建了游戏的两种模式,但直到现在,如果我们运行程序,什么都不会发生。

# 因此,让我们通过实现模式选择并根据选择实例化类来完成程序。

# 选择实现

# 用户将能够选择要玩的模式。让我们看看如何实现它。

if __name__ == “__main__”:

print(“选择游戏模式”)

choice = input(“[1],[2]: “)

if choice == “1”:
game = RandomMultiplication()
elif choice == “2”:
game = TableMultiplication()
else:
print(“请选择有效的游戏模式”)
exit()

game.run()

# 首先,我们要求用户在1或2个模式之间进行选择。如果输入无效,则脚本停止运行。如果用户选择了第一种模式,程序将运行”Random Multiplication”游戏模式,如果选择了第二种模式,则运行”Table multiplication”模式。

# 下面是它的外观。

# 贺喜你,你刚刚用面向对象编程完成了一个项目。

# 所有的代码都可以在链接中找到。

# 在本文中,您学会了:

# 使用Python类构造函数
# 使用面向对象的方式创建一个功能性的应用程序
# 在Python类中使用super函数
# 应用继承的基本概念
# 实现类和实例属性

# 祝你编码愉快👨‍💻

# 接下来,探索一些提高生产力的链接。

类似文章