原文:
zh.annas-archive.org/md5/D0EC374B077B4C90AC07697977B27176译者:飞龙
第六章:设计解决方案和解决方案过程
在本章中,我们将使用先前学到的内容,如问题的分析和计算思维过程,来设计解决方案。我们将结合逻辑处理来创建决策过程的视觉表示,以指导我们的算法设计。讨论的视觉表示包括图表、流程图和其他有用的过程。在本章中,我们将学习解决方案设计的关键要素;如何创建、使用和应用图表来处理和设计解决方案;我们将看看如何将解决方案设计过程应用于各种问题。
在本章中,我们将涵盖以下主题:
-
设计解决方案
-
绘制解决方案
-
创建解决方案
为了进一步了解算法和解决方案设计,我们需要更仔细地研究问题的前端。我们将从深入讨论设计解决方案的过程开始。
技术要求
您将需要最新版本的 Python 来运行本章中的代码。您可以在此处找到本章中使用的完整源代码:github.com/PacktPublishing/Applied-Computational-Thinking-with-Python/tree/master/Chapter06
设计解决方案
当我们设计解决方案时,我们经常使用设计思维模型,即使我们并不总是意识到。设计思维由不同的模型描述,但我们将看到最常见的五步模型。
与计算思维结合,设计思维过程可以帮助我们在开始绘制解决方案之前发展我们的想法。需要注意的是,我们不像在计算思维中那样线性地进行设计思维过程。想想计算思维的步骤:
-
问题分解
-
模式识别
-
抽象
-
算法设计
我们在之前的章节中定义了所有这些步骤,最近是在第五章的介绍中,探索问题分析。再次看到它们,我们知道在编写和设计算法时可以返回到分解。这就是我们所说的非线性过程。
设计思维模型也是如此。它是由斯坦福大学哈索-普拉特纳设计学院设计的。该模型的主要步骤包括以下内容:
-
共情:从受众或利益相关者的角度理解问题。
-
定义:确定目标、需要做出的决策、引入的任何偏见以及与问题相关的任何细节。
-
构思:进行头脑风暴,与我们将在本章的下一节中进行的图表化工作相呼应。
-
原型:设计算法解决方案并经常检查它。
-
测试:在整个过程中经常检查您的算法,并根据需要返回到以前的步骤。
正如您所看到的,我已经调整了设计思维模型,使其更符合计算思维过程。当我们使用这些模型并将它们结合在一起时,主要目标是将更难的问题分解为更简单的部分,以便我们可以设计和解决最佳算法。这并不取代计算思维。它只是提供了更好的想法,让我们了解如何处理这个过程。以下图表可以帮助演示这个过程可能如何工作:
图 6.1 - 设计思维模型
正如您所看到的,与最常见的线性模型不同,前面的模型将过程显示为循环。也就是说,回到共情可以发生在任何时候,因此在这些步骤之间来回移动是使用设计思维模型的最佳方式。
让我们看一个场景,在这个场景中,我们将设计思维与计算思维结合使用。
问题 1-营销调查
假设您正在与一家营销公司合作,他们要求您设计一份调查,以收集有关网站的反馈。以下是您可能要经历的一些步骤:
-
确定利益相关者:这包括您将调查的人员以及调查后将使用信息的人员,例如。
-
确定问题:这是您定义希望从调查中获得的信息的地方。
-
设计调查:这不仅包括您确定的问题,还包括调查的美学外观。
-
信息收集:这是您决定如何与将填写调查的人沟通的地方,例如电子邮件、网站上的链接或类似方式。
-
数据分析:您可以编写 Python 算法来帮助您进行数据分析,包括根据收集的数据创建表格和图表。
-
数据共享:这是您将计划向最初的利益相关者展示的可视化、报告和数据呈现的地方。
让我们明确一点:这是对过程的过度简化。但是假设您意识到需要为调查增加另一组。比如,最初只从学校的学生那里获得反馈,但后来意识到您想要增加教师和家长。那么,您将回到步骤 1,确定您的其他信息将受到哪些影响。您可能想要改变调查的外观,或者为成年人和儿童添加不同的调查。您可能需要添加只针对某一组的问题,这会影响您在调查算法中的决策。
现在让我们看看设计思维过程中的这些步骤。
对于我们的问题,确定利益相关者和问题是设计思维模型的步骤 1、2和3的一部分:共情、定义和构思。构建算法既是原型和测试的一部分,也是步骤 4和5。将人员添加到调查中会让我们回到步骤 1-3。循环重复,直到我们为我们的情景拥有一个可行的算法。在整个计算思维模型中,并使用其中的元素,您将使用嵌入其中的设计思维过程。这是决策过程的自然部分。
现在我们已经看过设计思维模型,让我们来看看如何使用图解解决方案来直观地表示决策过程。
图解解决方案
当我们设计算法时,我们经常使用图表和流程图来帮助我们分析过程,并直观地看到我们的决策是在哪里做出的。这些图表使我们能够创建更好的算法。您会记得,在第五章中,当我们正在建立一个商店时,我们创建了一个流程图(图 5.1和图 5.2)。
创建这些图表的过程因开发人员或编码人员而异。例如,我通常会为问题创建头脑风暴,然后从中制作流程图。为了了解这个过程,让我们回到本章前面的调查问题。看看以下头脑风暴。它并不完整,因为您可以添加许多子主题。这个头脑风暴假设我们正在调查利益相关者,以评估和分享对学校网站的反馈。
图 6.2 -头脑风暴图
从图表中可以看出,有许多考虑要做。实际的调查设计可能由我们作为程序员提供,或者我们可能参与设计调查。如果我们有了调查,我们的头脑风暴可能会有所不同,因为我们在问题中导航,并决定如何最好地将它们放置在算法中。这是共情过程的一部分。我们从多个角度、多个利益相关者的角度来看待我们的信息,并决定如何编写算法来帮助我们达到我们的目标。头脑风暴这样的非正式图表的目的是让我们在尝试创建更详细和有组织的流程图之前开始组织想法。当我们工作在图表上时,我们正在定义和构思我们的算法。这就是为什么在开始直接编码之前勾画出我们的计划是很重要的。
关于流程图,当我们讨论在 Python 中创建商店时,上一章中我们看到了一些流程图。现在让我们看一下基于一些决策的决策流程图。
重要的是要注意,从零开始创建调查可能是困难的。部分原因是可能有依赖于彼此的问题。例如,假设你要求用户说明他们是否赞成颜色选择。如果他们赞成,你可以继续。但如果他们不赞成,你可能想提供其他颜色方案供审查。这个问题只会出现在选择否选项的人身上。如果我们要处理头脑风暴的所有信息,我们的流程图可能会相当复杂,所以我们将专注于头脑风暴的外观类别中的一些问题。看一下下面的流程图:
图 6.3 - 调查的一个元素的流程图
从流程图中可以看出,有些事情并不清晰,比如完成一个问题后会发生什么,每个决定后你会去哪里等等。当我创建流程图时,有时会添加箭头来帮助我看到每个步骤之后会发生什么。下面的流程图显示了添加的一些箭头:
图 6.4 - 带箭头的流程图
从前面的流程图可以看出,并非所有箭头都被添加,但要仔细看颜色方案。如果用户同意颜色方案,那么他们直接进入字体部分。如果他们不同意,他们会被显示选项。假设一次只显示一个选项,那么用户在选择喜欢的选项后会进入字体部分。还可以添加提示,询问用户是否想再次查看选项,这会将他们带回备选方案 1。箭头可以被添加以显示这些细节。
这完全取决于作为开发人员和程序员的你自己最容易理解的方式。如果你是作家,把这些当作你的日记笔记。你组织想法的方式可能是个人的,只要确保你的最终结果和算法可以被意图使用的人轻松使用。
现在让我们看看如何将所有内容整合在一起,并为一些问题设计解决方案。
创建解决方案
当我们面对问题时,我们希望创建解决方案,解决我们所提供的信息,并提供一切所需并且易于用户理解的算法。在本节中,我们将利用本章学到的内容来设计解决问题的解决方案。
当我们使用我们的头脑风暴和流程图创建这些解决方案时,我们应该考虑以下内容:
-
我们计划的解决方案是否解决了问题?
-
解决方案设计是否显示了算法成功的清晰路径?
如果对这些问题的答案是“是”,那么我们可以开始编写解决方案。请记住,我们需要尽可能经常地测试算法。在编写算法时,请记住以下几点:
-
添加注释以标识您可能需要返回的部分,并清楚地帮助识别和定义您的变量、字典、函数和任何关键组件。
-
检查一下您是否有任何错误,比如第五章**中讨论的那些错误,探索问题分析。
-
尽可能经常运行您的程序以测试错误。
对于解决方案过程,我们将使用一个与本章早期工作的调查略有不同的问题。随着我们在本书中的学习,我们将解决您可以用于该问题的组件,比如添加图像、显示图形等。但是现在,让我们继续使用一些更基本的 Python 来练习创建解决方案的过程。
问题 2 - 比萨订单
我知道 - 食物。但这是演示逻辑和算法创建的最佳方式之一,所以请耐心等待。假设我们有一家比萨店。我们只卖一种类型的面团,因为我们是一种特色的地方。我们有两种不同尺寸的比萨:个人和家庭。有两种酱料选项:番茄酱和大蒜奶油。有三种奶酪选项:无奶酪、普通奶酪和额外奶酪。
选择的五种配料有:蘑菇、意大利辣香肠、洋葱和辣椒。不,我们的比萨店不会放橄榄。
让我们解决这个问题。我们希望有一个算法来捕捉用户选择的比萨订单选项。我们现在不考虑成本和订单中的其他项目,比如额外的比萨、饮料、甜点等。
这是我们知道的:
-
尺寸:个人或家庭
-
酱料:番茄酱或大蒜奶油
-
奶酪:无奶酪、普通奶酪、额外奶酪
-
配料:蘑菇、意大利辣香肠、洋葱、辣椒
现在我们有了这些,让我们看一下带有信息的流程图:
图 6.5 - 比萨店决策流程图
正如您所看到的,该图显示了这个特定问题的一个相当线性决策过程。我们还没有考虑的一件事是询问用户是否希望进行任何更改。这可能需要在每个步骤中发生。比如在选择奶酪时改变主意,改为选择番茄酱而不是大蒜奶油酱。您需要有一种方法可以返回,因此我们需要在创建算法时牢记这一点。
请记住,我们目前仅使用文本代码,因此我们现在将使用用户的数字和字母输入。但是,有办法将 Python 整合到更强大的算法中,这些算法包括图像、按钮等。
看一下算法中的以下代码片段:
ch6_pizzeria.py
#Get input for your variables for size and sauce first.
size_choice = str(input("Is this a personal or family pizza? Type personal or family. "))
sauce_choice = str(input("Which sauce would you like? Marinara or garlic cream? Type m for marinara and g for garlic cream. "))
if sauce_choice == "g":
sauce = "garlic cream"
else:
sauce = "marinara"
#The cheese choice will dictate a few more options. Define the variable first.
cheese_choice = str(input("Would you like cheese on your pizza? Type y for yes and n for no. "))
请注意,在代码片段中,我们首先定义了尺寸和酱料。我在这里重申,有其他处理这个特定逻辑过程的方法。例如,我们可以将一些变量保存到字典中,并使用数组。目前,我们正在使用到目前为止学到的知识来创建我们的算法,但是在本书的后面,我们将有机会了解其他方法。
前面的代码片段有最终的奶酪选择。无论选择哪个选项,我们都需要对配料做出决定。这将需要发生两次,因为我们需要为“是”和“否”都需要。
看一下以下代码片段,这是前面代码的延续:
ch6_Pizzeria.py
#Toppings need to happen whether or not you want cheese.
if cheese_choice == "y":
cheese2_choice = str(input("Would you like regular cheese or extra cheese? Type r for regular and e for extra cheese. "))
if cheese2_choice == "r":
cheese = "regular cheese"
else:
cheese = "extra cheese"
toppings1_input = str(input("Would you like mushrooms on your pizza? Type y for yes and n for no. "))
if toppings1_input == "y":
toppings1 = "mushrooms"
else:
toppings1 = "no mushrooms"
else:
cheese = "no cheese"
if cheese_choice == "n":
toppings1_input = str(input("Would you like mushrooms on your pizza? Type y for yes and n for no. "))
if toppings1_input == "y":
toppings1 = "mushrooms"
else:
toppings1 = "no mushrooms"
print("You want a " + size_choice + " pizza with " + sauce + " sauce, " + cheese + ", and " + toppings1 + ".")
正如您从代码片段中看到的,我们只使用了蘑菇。在选择家庭尺寸、大蒜酱、普通奶酪和蘑菇后,这个特定代码的输出如下:
Is this a personal or family pizza? Type personal or family. family
Which sauce would you like? Marinara or garlic cream? Type m for marinara and g for garlic cream. g
Would you like cheese on your pizza? Type y for yes and n for no. y
Would you like regular cheese or extra cheese? Type r for regular and e for extra cheese. r
Would you like mushrooms on your pizza? Type y for yes and n for no. y
You want a family pizza with garlic cream sauce, regular cheese, and mushrooms.
使用提供的代码并查看输出,尝试组合其余四种配料的代码。我猜,如果您正在制作自己的比萨饼,欢迎您更改这里提供的选项。只需将橄榄留给自己。
现在,如前所述,我们可能需要返回并进行更改。让我们看一段为您做到这一点的代码片段:
ch6_Pizzeria2.py
ready_end = str(input("Do you need to make any changes? Type y for yes and n for no. "))
if ready_end == "y":
size_choice = str(input("Is this a personal or family pizza? Type personal or family. "))
sauce_choice = str(input("Which sauce would you like? Marinara or garlic cream? Type m for marinara and g for garlic cream. "))
if sauce_choice == "g":
sauce = "garlic cream"
else:
sauce = "marinara"
cheese_choice = str(input("Would you like cheese on your pizza? Type y for yes and n for no. "))
从代码片段中可以看出,需要对所需的更改进行决策。如果是,则再次提出问题。如果不是,则为用户打印选项。查看完整运行程序的以下输出:
Is this a personal or family pizza? Type personal or family. family
Which sauce would you like? Marinara or garlic cream? Type m for marinara and g for garlic cream. g
Would you like cheese on your pizza? Type y for yes and n for no. n
Would you like mushrooms on your pizza? Type y for yes and n for no. y
Do you need to make any changes? Type y for yes and n for no. y
Is this a personal or family pizza? Type 1 for personal and 2 for family. family
Which sauce would you like? Marinara or garlic cream? Type m for marinara and g for garlic cream. m
Would you like cheese on your pizza? Type y for yes and n for no. n
Would you like mushrooms on your pizza? Type y for yes and n for no. y
You want a family pizza with marinara sauce, no cheese, and mushrooms.
如代码所示,问题被问了两次,因为我们在选项中做了更改。根据您想要问这个问题的频率,您需要继续重复部分代码。有简化的方法,我们将在 Python 语言程序章节(第八章,Python 简介)和后续章节中更深入地讨论这些选项。
在我们继续之前,让我们再看一个问题,重新经历一遍设计过程。
问题 3 - 延迟和 Python
我在 Python 中遇到的第一个问题之一是创建一个根据所选颜色而有不同反应的算法。这类似于如果您正在创建交通灯时会遇到的情况。每个灯的延迟都不同。所以让我们创建一个解决这个问题的算法。我们将使其成为用户选择的颜色,介于绿色、黄色和红色之间,只是为了保持交通灯的主题。因此,让我们做一些假设:
-
绿色将意味着 5 秒的延迟
-
黄色将意味着 2 秒的延迟
-
红色将意味着 4 秒的延迟
这些特定延迟没有特定的原因;我只是想让它们都在 5 秒以内。现在,假设我们正在玩一个游戏,用户必须选择一种颜色。如果他们选择黄色或红色,他们将会有延迟,然后会再次被问及。目标是从程序中获得“您赢了!您现在可以走了”的消息。因此,让我们为此创建一个流程图:
图 6.6 - 交通灯游戏的流程图
从流程图中可以看出,如果选择黄色或红色,游戏将重新开始。现在我们已经了解了游戏的基本情况,我们需要编写代码。
重要提示:
为了能够使用延迟,我们需要导入time库。使用代码import time来做到这一点。要包含延迟,我们使用代码time.sleep()。
让我们看一段代码片段:
ch6_sleep.py
import time
print("Let's play a game. Choose a color to learn your destiny. Choose wisely or you'll have to start over. ")
i = 0
while i < 4:
color = str(input("Choose a color: red, green, or yellow. "))
if color == "green":
print("You must wait 5 seconds to learn your fate.")
time.sleep(5)
print("You win! Excellent choice!")
break
elif color == "yellow":
print("You must wait 2 seconds to learn your fate.")
time.sleep(2)
print("You lose! You must start over.")
i = i + 1
else:
print("You must wait 4 seconds to learn your fate.")
time.sleep(4)
print("You lose! You must start over.")
i = i + 1
如您所见,该算法包含了我们在之前章节中讨论循环、布尔语句等时看到的一些代码。如果用户没有赢得游戏,这段特定代码将在三轮后返回到开头。我们使用if-elif-else语句来处理颜色情况。游戏进行三轮的输出如下:
Let's play a game. Choose a color to learn your destiny. Choose wisely or you'll have to start over.
Choose a color: red, green, or yellow. yellow
You must wait 2 seconds to learn your fate.
You lose! You must start over.
Choose a color: red, green, or yellow. red
You must wait 4 seconds to learn your fate.
You lose! You must start over.
Choose a color: red, green, or yellow. green
You must wait 5 seconds to learn your fate.
You win! Excellent choice!
从游戏输出中可以看出,所有三轮都已经玩过了。每个延迟都按照陈述发生,您需要自己测试,因为我无法用文本显示时间延迟。
制作流程图使得创建这个算法比我一开始读问题后立即编写代码更简单。在编写算法之前,习惯于详细阐述您需要的过程非常重要。设计解决方案可能是一个漫长而乏味的过程,但我们在开始时越有条理,我们的算法就会越好。
总结
在本章中,我们讨论了如何设计、绘制和创建解决问题的解决方案。我们讨论了设计思维的非线性过程,以了解如何最好地设计解决方案。设计思维模型是一个五步过程:共情,定义,构思,原型和测试。在计算思维过程中使用这个五步过程可以帮助我们避免许多问题和陷阱。
我们还创建了头脑风暴和流程图,以建立我们的算法决策过程来解决问题。
在下一章中,我们将利用我们对算法设计和解决方案设计的知识,来识别解决方案中的挑战并调试程序。
第七章:识别解决方案中的挑战
在本章中,我们将评估算法和图表,同时学习如何避免一些常见错误,并确定是否可以对现有算法进行可能的调整以简化它。我们将根据问题描述评估解决方案,以验证解决方案是否与问题一致。我们将学习如何识别解决方案设计过程中的陷阱。值得注意的是,我们将在本书的第二部分,应用 Python 和计算思维,以及本书的第三部分,使用计算思维和 Python 进行数据处理、分析和应用中深入探讨本章的内容,进一步深入了解Python编程语言。
要了解调试,让我们提醒自己计算思维过程并不是线性的。即使我们从原始问题出发,有时我们会重新定义问题,或者需要根据算法所针对的人口变化或者我们想要调整算法设计来调整泛化。但有时,我们会在设计并使用算法后解决问题。根据我们的角色,我们将评估算法的错误、需要的更改等等。了解如何找到和分析错误可以帮助我们,无论我们是绝对的 Python 初学者还是在职业生涯中深入研究。
在本章中,您将学习如何识别和修复程序中的错误,以及如何避免算法设计中的陷阱。
在本章中,我们将涵盖以下主题:
-
识别算法设计中的错误
-
调试算法
-
比较解决方案
-
完善和重新定义解决方案
技术要求
您需要最新版本的 Python 来运行本章中的代码。您可以在此处找到本章中使用的完整源代码:github.com/PacktPublishing/Applied-Computational-Thinking-with-Python/tree/master/Chapter07
识别算法设计中的错误
对于任何编码人员来说,算法中的错误都是生活中的一个事实。熟悉犯错是很重要的。正如在第五章中提到的,探索问题分析,以及第六章中提到的,解决过程和设计,测试算法并经常测试是一个好习惯。等到完成了数百或数千行代码才测试某些东西是灾难的预兆。是的,我曾经在复制一个游戏时根本没有测试。直到我复制了 4585 行代码。我当时很年轻。说实话,我从未找到我犯的错误。我重新开始,并在每个角落都开始测试。第二次成功了,但我浪费了数周时间复制一切(那是从一本书上复制的——当时还没有 GitHub),然后试图找出错误。所以请不要成为我。请测试您的算法。
现在,在进行调试和处理代码之前,让我们先看看在解决问题时可能遇到的错误。
在本节中,我们将重点关注以下两类广泛的错误:语法错误和逻辑错误。
语法错误
有时语法错误被称为解析错误。当我们忘记缩进、添加冒号、为字符串添加引号等等时,就会产生错误。让我们看看以下各种类型的语法错误。
使用冒号
在 Python 中,冒号用于分隔条件、创建循环等等。冒号是一种告诉算法下一步是这个特定代码块的方式。当我们在 Python 中引入冒号时,它会自动缩进我们代码的下一行。但如果我们忘记在需要的地方包括冒号,程序将无法成功运行。让我们看一个语法错误的例子:
for i in range(1, 10)
print(i)
如果我们运行这段代码,会得到一个错误消息,说invalid syntax。下面的截图显示了当我们尝试运行这个程序时出现的弹出窗口:
图 7.1 - 错误弹出窗口
如果我们从 Python shell 中运行这个程序,错误会如何显示:
SyntaxError: invalid syntax
正如你所看到的,Python 程序会在我们的代码中包含错误时提醒我们。
请注意,在代码中range后面缺少一个冒号。现在,看一下以下代码中的修正语法:
ch7_syntaxerror1.py
for i in range(1, 10):
print(i)
当我们运行修正后的代码时,程序会运行并打印出数字 1 到 9,如下所示:
1
2
3
4
5
6
7
8
9
你可能还记得range函数不包括上限端点。如果我们想打印数字 10,我们的范围需要是range(1, 11)。
现在,让我们看一看 Python 中使用的其他标点符号,可能会导致一些错误,即括号、嵌套括号和括号。
使用嵌套括号和括号
除了涉及冒号的语法错误外,还有嵌套括号的错误。我们必须始终检查每个开括号是否有闭括号。对于括号也是如此。让我们看一下下面的代码,其中包含括号错误:
name = str(input('What is your name? ')
print(name)
正如你所看到的,名称定义中有两个开括号,但只有一个闭括号。当我们运行该程序时,Python 会报告一个无效的语法错误。当我们在 Python shell 或解释器中运行该程序时,会发生什么:
SyntaxError: invalid syntax
现在这是没有错误的相同代码,注意我们甚至将str()去掉了,因为它是不需要的,这样简化了我们的代码,同时消除了错误。
ch7_syntaxerror2.py
name = input('What is your name? ')
print(name)
现在当我们运行代码时,程序会要求输入名称,然后打印出来。输出如下所示:
What is your name? Monique
Monique
正如你所看到的,程序现在可以正常运行了。
在[第三章](B15413_03_Final_SK_ePub.xhtml#_idTextAnchor056)理解算法和算法思维中,我们使用字典创建了一个带有每个菜单项定价的菜单。字典包含括号,用于表示字典开始和结束的位置。让我们看一下几行代码:
cars = {
"Hyundai": "Kona",
"Honda": "CR-V",
"Toyota": "Camry"
print(cars)
如果我们看一下程序,字典缺少闭括号},所以我们会得到一个语法错误,就像我们之前的例子一样。以下片段显示了已纠正的程序:
ch7_syntaxerror3.py
cars = {
"Hyundai": "Kona",
"Honda": "CR-V",
"Toyota": "Camry"
}
print(cars)
正如你所看到的,一旦添加了括号,程序就会运行并打印出以下输出:
{'Hyundai': 'Kona', 'Honda': 'CR-V', 'Toyota': 'Camry'}
字典中的每个条目都打印在一行中,用逗号分隔。在编写算法时,添加print语句是有帮助的,以确保我们没有任何错误。一旦测试过,我通常会删除不必要的打印函数,但在编写长算法并需要测试以避免问题时,它们确实很有用。
在 Python 编写算法时,还有许多其他错误。让我们再看看一些语法错误。
其他语法错误
在更长的程序中,可能会引入许多其他语法错误。例如,如果你看一下我们刚刚使用的字典,忘记逗号也会创建一个语法错误。通常,当我们尝试运行程序时,这些语法错误会很快被识别出来。Python 会突出显示缩进的位置或者括号缺失的地方。语法错误通常很容易识别,但还有许多其他类型的错误。
逻辑错误
在[第四章](B15413_04_Final_SK_ePub.xhtml#_idTextAnchor071)理解逻辑推理中,我们讨论了可能遇到的逻辑错误:
-
在等式或语句中使用错误的变量
-
使用错误的运算符来测试条件
-
在检查条件时使用错误的缩进
现在我们将看一下逻辑中的其他错误,这些错误在 Python 中有特定的调用,以及每个错误代表什么。
逻辑错误也称为运行时错误。下表显示了 Python 中一些内置错误以及它们的表示:
https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Table_7.1.01.jpghttps://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Table_7.1.02.jpg
表 7.1 - 异常和原因/描述表
如你所见,在 Python 中有许多不同类型的错误被标记为异常。你可以通过运行以下代码获取 Python 异常列表:
ch7_errors.py
print(dir(locals()['__builtins__']))
当我们运行该代码时,输出提供了以下错误值:
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
如前所述,这些是 Python 中的内置异常。有一种方法可以定义我们自己的异常,但在这本书中我们不会涉及到它们。
请注意,这些不是我们在编程时会遇到的唯一错误。我们可能会因为自己在计算中犯的错误而出现错误,就像我们在第四章中讨论的那样,理解逻辑推理。我们可能会在布尔逻辑中引入错误。目标是尽量避免这些错误,使我们的程序能够正常运行。记住,测试你的算法,并经常测试。
现在让我们看一些带有错误的算法,并尝试识别错误,以便我们可以纠正它们。
调试算法
在 Python 中,我们可以使用breakpoint()函数(内置的)运行调试器。我们可以将这段代码引入到我们的程序中,并在我们对代码不确定的地方插入它。添加breakpoint()将检查错误和 bug。当我们运行breakpoint()函数时,我们会得到一个pdb输出,代表Python 调试器。需要注意的是,这个内置函数出现在Python 3.7和更新版本中。Python 3.6和更早版本的先前调试器是pdb.set_trace()。
当我们运行调试器时,我们可以使用四个命令:
-
c:继续执行 -
q:退出调试器/执行 -
n:在函数内部执行下一行的步骤 -
s:在这个函数或被调用的函数中执行下一行的步骤
让我们看一下代码,并运行所列出的每个命令:
ch7_debugger.py
number = 5
number2 = 'five'
print(number)
breakpoint()
print(number2)
看看这段代码,你可以看到print(number)后面的breakpoint()命令。代码将正常运行,直到达到breakpoint()命令。在这个阶段,执行停止。如果我们按下c键,那么它将继续运行程序。看看输出是什么样子。
请注意,在代码中有两个斜杠之间有三个点,/…/。这是因为路径可能会因计算机不同而不同。你的路径将包括程序所在的完整路径:
5
> /Users/.../Python/ch7_debugger.py(8)<module>()
-> print(number2)
(Pdb) c
five
如你所见,它继续打印字符串five,因为它只是继续运行程序。现在让我们看看当我们运行q命令时的输出,它会退出程序:
5
> /Users/.../Python/ch7_debugger.py(8)<module>()
-> print(number2)
(Pdb) q
Traceback (most recent call last):
File "/Users/.../Python/ch7_debugger.py", line 8, in <module>
print(number2)
File "/Users/.../Python/ch7_debugger.py", line 8, in <module>
print(number2)
bdb.BdbQuit
正如你所看到的,一旦我们使用q命令,由于程序退出,我们会得到一个回溯错误。它打印了breakpoint()代码上面的行,但没有打印第二个print(number2)命令。现在,让我们看看当我们输入n时会发生什么,它应该会带我们到下一行:
5
> /Users/.../Python/ch7_debugger.py(8)<module>()
-> print(number2)
(Pdb) n
five
--Return--
> /Users/.../Python/ch7_debugger.py(8)<module>()->None
-> print(number2)
(Pdb)
如你所见,当我们输入n时,程序继续运行并打印第二个命令行。当这样做时,你可以看到-> None输出和运行的代码:print(number2)。最后,让我们看一下稍微改变的代码,看看在运行调试器时使用s会发生什么:
ch7_debugger2.py
number = 5
number2 = 'five'
print(number)
breakpoint()
print(str(number) + number2)
当我们运行这个程序和调试器时,如果我们使用s,我们会得到以下输出:
5
> /Users/.../Python/ch7_debugger2.py(8)<module>()
-> print(number + " " + number2)
(Pdb) s
TypeError: unsupported operand type(s) for +: 'int' and 'str'
> /Users/.../Python/ch7_debugger2.py(8)<module>()
-> print(number + " " + number2)
(Pdb)
如你所见,程序遇到了TypeError并提供了更多信息。我尝试将整数和字符串组合在一起。因此,我们需要修复代码以正确运行。在我这样做之前,让我们看看当我尝试使用c继续代码时会发生什么:
5
> /Users/.../Python/ch7_debugger2.py(8)<module>()
-> print(number + " " + number2)
(Pdb) c
Traceback (most recent call last):
File "/Users/.../Python/ch7_debugger2.py", line 8, in <module>
print(number + " " + number2)
TypeError: unsupported operand type(s) for +: 'int' and 'str'
如你所见,我可以从两个命令中获得相同的信息,程序的响应略有不同。要解决这个问题,我必须将数字转换为字符串,在print行中可以使用以下代码来实现:
ch7_debugger3.py
number = 5
number2 = 'five'
print(number)
breakpoint()
print(str(number) + " " + number2)
现在,我已经修复了代码,使得打印行中的项目都是字符串,当我使用c继续时,输出如下:
5
> /Users/.../Python/ch7_debugger3.py(8)<module>()
-> print(str(number) + " " + number2)
(Pdb) c
5 five
如你所见,程序现在打印了正确的信息,将数字作为字符串与five字符串组合在一起。双引号在它们之间添加了一个空格,我们以前已经见过,但当我们在第八章中查看 Python 基础知识时,将再次讨论。
现在,让我们看一下相同问题的一些解决方案,以便分析它们。
比较解决方案
当我们看问题时,我已经提到在 Python 中有多种方法可以做同样的事情。根据我们试图实现的目标,一些命令可能比我们的算法中的其他命令更好。让我们首先看一下一个问题的几种解决方案。
问题 1 - 打印偶数
你被要求编写一个算法,根据用户提供的范围打印偶数。也就是说,如果用户输入范围为 2 到 20,那么程序将打印 2, 4, 6, 8, 10, 12, 14, 16, 18 和 20。让我们假设如果端点是偶数,我们希望包括端点。
让我们看一下两种可能解决方案中的第一种。记住,一个解决方案可能不比另一个更好。很大程度上取决于你的完整算法的目标。*列表更合适吗?字典?函数?*当我们设计解决方案时,这些问题很重要。
算法解决方案 1 - 打印偶数
回想一下,我们将接受用户输入来创建一个给定范围内的偶数列表。看一下以下代码,它要求用户输入,然后打印出数字:
ch7_evenalgorithm1.py
print("This program will print the even numbers for any range of numbers provided.")
endpoint1 = int(input("What is the lower endpoint of your range? "))
endpoint2 = int(input("What is the upper endpoint of your range? "))
endpoint2 = endpoint2 + 1
for i in range(endpoint1, endpoint2):
if i % 2 == 0:
print(i)
注意,endpoint2被转换为endpoint2 + 1。这是因为如果我们不添加1,那么如果它是一个偶数,上限端点将不会被包括在内。程序还以用户的打印消息开始,说明程序的功能是什么。
当我用端点2和6运行这个程序时,我得到以下输出:
This program will print the even numbers for any range of numbers provided.
What is the lower endpoint of your range? 2
What is the upper endpoint of your range? 6
2
4
6
如你所见,两个端点都是偶数且包括在内。如果我们用端点3和9运行程序,我们得到以下输出:
This program will print the even numbers for any range of numbers provided.
What is the lower endpoint of your range? 3
What is the upper endpoint of your range? 9
4
6
8
尽管终点现在在技术上是10,但范围的上限并不包括在内,因此在10以下的最大偶数是8。现在,我可以为一个更大的范围运行这个程序,但是范围越大,滚动以获取所有数字就越困难。因此,让我们看一种不同的方法来获取我们的偶数。
算法解决方案 2 - 打印偶数
正如我们从前面的例子中看到的,每个偶数都被打印到不同的行。让我们看看是否可以改变这一点,而是创建一个列表。Python 中的列表可以是空的。我们可以为它们使用任何名称,然后将它们等于括号内的项目或只是空括号。
例如,我可以创建一个名为evenNumbers = []的空列表。让我们看看以下算法中的情况:
ch7_evenalgorithm2.py
print("This program will print the even numbers for any range of numbers provided.")
endpoint1 = int(input("What is the lower endpoint of your range? "))
endpoint2 = int(input("What is the upper endpoint of your range? "))
endpoint2 = endpoint2 + 1
evenNumbers = []
for i in range(endpoint1, endpoint2):
if i % 2 == 0:
evenNumbers.append(i)
print(evenNumbers)
你可以看到代码的前几行是相同的。在这个特定的代码中唯一的区别是数字的打印方式。列表是在for循环之前创建的。然后,使用evenNumbers.append(i)代码将每个数字附加到列表中。最后,我们打印我们的列表以获得以下输出:
This program will print the even numbers for any range of numbers provided.
What is the lower endpoint of your range? 2
What is the upper endpoint of your range? 10
[2, 4, 6, 8, 10]
如你所见,所有偶数都包含在一个列表中,这比一个接一个地打印更容易阅读。想象一下,如果你必须打印范围在 300-1,000 之间的偶数。当我们运行程序时,列表会使阅读更容易。对于第二个算法,输出如下:
This program will print the even numbers for any range of numbers provided.
What is the lower endpoint of your range? 300
What is the upper endpoint of your range? 1000
[300, 302, 304, 306, 308, 310, 312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 332, 334, 336, 338, 340, 342, 344, 346, 348, 350, 352, 354, 356, 358, 360, 362, 364, 366, 368, 370, 372, 374, 376, 378, 380, 382, 384, 386, 388, 390, 392, 394, 396, 398, 400, 402, 404, 406, 408, 410, 412, 414, 416, 418, 420, 422, 424, 426, 428, 430, 432, 434, 436, 438, 440, 442, 444, 446, 448, 450, 452, 454, 456, 458, 460, 462, 464, 466, 468, 470, 472, 474, 476, 478, 480, 482, 484, 486, 488, 490, 492, 494, 496, 498, 500, 502, 504, 506, 508, 510, 512, 514, 516, 518, 520, 522, 524, 526, 528, 530, 532, 534, 536, 538, 540, 542, 544, 546, 548, 550, 552, 554, 556, 558, 560, 562, 564, 566, 568, 570, 572, 574, 576, 578, 580, 582, 584, 586, 588, 590, 592, 594, 596, 598, 600, 602, 604, 606, 608, 610, 612, 614, 616, 618, 620, 622, 624, 626, 628, 630, 632, 634, 636, 638, 640, 642, 644, 646, 648, 650, 652, 654, 656, 658, 660, 662, 664, 666, 668, 670, 672, 674, 676, 678, 680, 682, 684, 686, 688, 690, 692, 694, 696, 698, 700, 702, 704, 706, 708, 710, 712, 714, 716, 718, 720, 722, 724, 726, 728, 730, 732, 734, 736, 738, 740, 742, 744, 746, 748, 750, 752, 754, 756, 758, 760, 762, 764, 766, 768, 770, 772, 774, 776, 778, 780, 782, 784, 786, 788, 790, 792, 794, 796, 798, 800, 802, 804, 806, 808, 810, 812, 814, 816, 818, 820, 822, 824, 826, 828, 830, 832, 834, 836, 838, 840, 842, 844, 846, 848, 850, 852, 854, 856, 858, 860, 862, 864, 866, 868, 870, 872, 874, 876, 878, 880, 882, 884, 886, 888, 890, 892, 894, 896, 898, 900, 902, 904, 906, 908, 910, 912, 914, 916, 918, 920, 922, 924, 926, 928, 930, 932, 934, 936, 938, 940, 942, 944, 946, 948, 950, 952, 954, 956, 958, 960, 962, 964, 966, 968, 970, 972, 974, 976, 978, 980, 982, 984, 986, 988, 990, 992, 994, 996, 998, 1000]
我之所以只打印这一个而不是第一个算法,是因为第一个算法需要很多页,我们不想在这本书中浪费纸张。你可以看到其中一个比另一个更容易使用和更合适,因为更容易阅读更大的数字组。
这就是为什么我们需要审视我们所有的算法,并确定它们是否是表达我们所需的最佳方式。虽然有些算法可以工作,但它们可能不是最佳解决方案,有时这是可以接受的。但有时,进行一些更改,有时甚至是添加几行代码,就像我们在算法 2中所做的那样,可以显著改变我们的输出,并对我们更有帮助。
当我们比较这两个算法时,我们也在不断完善和重新定义我们的解决方案,这在下一节中我们会做更多。
精炼和重新定义解决方案
如果我们长时间观察算法,总是可以找到方法来完善和重新定义它们。想想我们手机上的应用有多少更新。总有人在玩弄这些应用,使它们更安全,增加游戏的关卡,更新艺术文件等等。作为程序员/编码人员,我们总是在努力让我们的工作变得更好。
我们将从一个算法开始这一节。以下程序打印出三只宠物的名字:
ch7_pets1.py
cat = "Whiskers"
dog = "King Kong"
bird = "Pirate"
print("The cat's name is " + cat + ", the dog's name is " + dog + \
", and the bird's name is " + bird + ".")
这个简单的代码中包含了一切,所以这次没有用户输入。你可以看到在print()命令中的dog +后使用了\字符。这个反斜杠允许我们在下一行添加剩余的代码,这样我们可以更容易地阅读它。
代码的输出如下:
The cat's name is Whiskers, the dog's name is King Kong, and the bird's name is Pirate.
正如你所看到的,这是一个简单的句子,带有宠物名字。
现在,假设我们有一只猫、一只狗和一只鸟,但它们的名字不一样。我们可以使用一个带有三个参数的函数。请记住,我们将在第八章中详细介绍函数的定义和信息,Python 简介。现在,让我们看看带有函数的算法。我们将函数命名为myPets()。看看以下算法:
ch7_pets2.py
def myPets(cat, dog, bird):
print("The cat's name is " + cat + ", the dog's name is " + dog +\
", and the bird's name is " + bird + ".")
myPets(cat = "Whiskers", dog = "King Kong", bird = "Pirate")
这个算法看起来与上一个非常相似,只是名字的定义在代码的最后一行。调用函数时,使用该行的信息来填写上面算法行中的定义的空白。输出看起来与上一个代码相同:
The cat's name is Whiskers, the dog's name is King Kong, and the bird's name is Pirate.
现在,正如你所看到的,这只打印了一个函数,因为我们只提供了一个信息,但我们可以随时调用函数,使用任意数量的值。看看这个算法:
ch7_pets3.py
def myPets(cat, dog, bird):
print("The cat's name is " + cat + ", the dog's name is " + dog +\
", and the bird's name is " + bird + ".")
myPets(cat = "Whiskers", dog = "King Kong", bird = "Pirate")
myPets(cat = "Mimi", dog = "Jack", bird = "Peyo")
myPets(cat = "Softy", dog = "Leila", bird = "Oliver")
正如你所看到的,现在函数将被调用三次。我们只有一个print()命令,但是函数定义意味着每次调用函数时都会使用print()命令。看看输出是什么样子的:
The cat's name is Whiskers, the dog's name is King Kong, and the bird's name is Pirate.
The cat's name is Mimi, the dog's name is Jack, and the bird's name is Peyo.
The cat's name is Softy, the dog's name is Leila, and the bird's name is Oliver.
请注意,当我们调用函数时,使用了三组宠物名字,打印了三个不同的句子。
当我们编写算法时,重要的是要考虑我们现在需要什么,以及我们将来可能需要什么。对于一个实例来说,使用第一个算法是可以的,但是如果我们想要为社区中的每个人或者教室中的每个学生运行算法,那么第二个算法更有帮助。重新定义我们的需求并完善我们的算法有助于改进我们从程序中得到的东西。
请注意,正如前面提到的,我们将在第八章中更多地讨论函数,Python 简介。我们将讨论的其中一件事是为未知数量的参数创建一个函数。*例如,如果我只有一只狗和一只鸟呢?*我们可以通过对算法进行一些更改来解决这个问题。我们很快就会研究这个问题。目前,我们只是稍微了解了为什么有时需要比较算法并重新定义和重新设计它们以更好地满足我们的需求。
摘要
在本章中,我们讨论了算法设计中的错误以及如何调试解决方案。我们还学习了如何比较解决方案,并在需要时对解决方案进行改进和重新设计。阅读完本章后,您应该更了解算法中的语法错误以及如何在Python 3.7及以上版本中使用breakpoint()命令来使用调试器。内置调试器为您提供了四种操作:c = 继续,q = 退出,n = 下一行,s = 步进。
使用调试器可以帮助我们确定代码中可能出错的地方。我们可以在代码的任何位置添加这行代码来确定问题所在。
我们还研究了提供相同输出但使用不同代码的算法。通过比较算法解决方案,我们可以确定哪些更有用,哪些更适合我们的问题或情况,以及为什么我们应该选择其中一个而不是另一个。请记住,算法是指令列表。在广泛使用算法的情况下,知道使用哪些指令至关重要。某些解决方案可能比其他解决方案更适合您的问题。考虑算法的目的、算法中的代码片段以及它们将如何在更大的算法中使用,并相应地做出决定。每个问题和每个解决方案都是独一无二的。
当我们完成本书的第一部分,计算思维导论时,我们已经了解了计算思维过程,始终关注可能的场景,以帮助我们理解该过程的有用性,如何进行头脑风暴并为决策创建流程图,以及如何设计我们的算法。在第二部分,应用 Python 和计算思维中,我们将更深入地研究 Python 语言,以便能够解决更复杂的问题,比如处理数据和函数的问题。我们还将更深入地了解 Python 编程语言,并将在后续章节中将这些知识应用于多种类型的问题。
第二部分:应用 Python 和计算思维
在选择编程语言时有多种选择。Python 编程语言是一种强大的开源面向对象的编程语言。它是一种高级编程语言,拥有越来越多的库和包,不断扩展其功能。我们可以使用 Python 来编写从简单的数学算法到复杂的数据科学算法,甚至机器学习的程序。了解 Python 也意味着了解计算思维的多个领域。
在本节中,您将学习如何在使用 Python 编程语言解决问题时使用计算思维过程。
本节包括以下章节:
-
第八章,Python 简介
-
第九章,理解输入和输出以设计解决方案算法
-
第十章,控制流
-
第十一章,使用计算思维和 Python 解决简单挑战
第八章:Python 简介
在本章中,我们将学习 Python 的命令和功能,并将它们应用到问题中。当我们深入第二部分,应用 Python 和计算思维的第一章时,我们将使用更复杂的 Python 编程。在本章中,我们将更多地关注语言,而其余章节将侧重于应用。
在本章中,我们将涵盖以下主题:
-
介绍 Python
-
使用字典和列表
-
使用变量和函数
-
学习文件、数据和迭代
-
使用面向对象的编程
当我们深入研究 Python 编程语言时,请记住,一些内容已经在之前的章节中涵盖过,比如我们在研究计算思维过程时使用的字典和函数。本章将帮助您更轻松地找到满足您计算思维问题需求的 Python 命令的关键信息。
技术要求
您需要最新版本的 Python 来运行本章中的代码。您可以在此处找到本章中使用的完整源代码:github.com/PacktPublishing/Applied-Computational-Thinking-with-Python/tree/master/Chapter08
介绍 Python
由于其易用性,Python 是增长最快的编程语言之一。Python 的吸引力之一是,我们可以用更少的代码行和更简单的语言和语法编写与 C、C++和 Java 等语言相同的程序。Python 的另一个吸引力在于它是可扩展的,这意味着我们可以为其添加功能和功能。
虽然并非所有功能都是内置的,但我们可以使用库来添加我们需要的功能。这些库可以下载并使用。例如,如果我们想要处理数据和数据科学,我们可以下载一些库,比如Pandas、NumPy、Matplotlib、SciPy、Scikit Learn等。但在我们深入研究这些库之前,让我们先了解一下 Python 语言的工作原理并学习其基础知识。
Python 具有一些内置的引用函数。下表按字母顺序列出了这些函数:
https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Table_8.1.jpg
表 8.1 - Python 内置函数
虽然我们不会在本书中涵盖所有的函数,但在研究 Python 及其算法时,我们会使用其中一些函数。让我们从这里列出的一些数学函数开始。
数学内置函数
在 Python 中,一些数学函数已经内置,比如abs()、eval()、max()、min()和sum()函数。这些并非所有内置的数学函数,但我们将仔细研究这些特定函数,以了解 Python 如何处理它们。
abs()函数将帮助我们找到一个数的绝对值,无论是整数还是浮点数。请看下面的代码片段:
ch8_absFunction.py
x = abs(-3.89)
print(x)
当我们运行这个程序时,我们将得到–3.89的绝对值。请记住,一个数的绝对值是它到 0 的距离。运行这个程序时,请看一下输出:
3.89
由于绝对值始终是正数,当我们运行abs(–3.89)时,我们得到3.89。
另一个有用的数学函数是eval()函数。我们可以在这个函数中定义一个变量,然后调用 Python 来使用该值评估代数表达式。在 Python shell 中,我们可以首先定义变量如下:
>>> p = 2
现在变量已经定义,我们可以用任何表达式调用eval()函数。例如,请看下面的输入和输出:
>>> eval('2 * p - 1')
如您所见,Python 使用了先前定义的p值2,并替换然后评估表达式以产生这个输出:
3
Python 程序也可以作为计算器使用,因此你可以像平常一样进行数学运算。以下是一些例子:
>>> 10-8
如你所见,Python 知道将破折号视为减法,并产生数学表达式的结果:
2
在下一个案例中,Python 将+解释为数学符号,并提供了求和表达式的结果:
>>> 4+5
输出是:
9
注意最后一个表达式10**5。在 Python 中,我们可以使用两个星号(**)表示指数:
>>> 10**5
输出是:
100000
现在,让我们看看max()函数。这个函数可以用在可迭代的列表上,但我们可以用只有两个数字的列表来测试它:
>>> max(12, 30)
你可以清楚地看到输出是什么:
30
让我们看另一个例子:
>>> max(100, 10)
这是输出:
100
从输出中可以看出,该函数总是选择最大的项。你可以添加第三个项,该函数仍然会选择最大值。这些内置函数在某种程度上很聪明,它们被编码为能够处理所提供的内容 - 例如两个或三个项 - 而无需明确告诉 Python 我们将引入多少项:
>>> max(230, 812, 109)
获得的输出是:
812
如你所见,我们不必向内置函数添加任何内容来找到三个值的最大值。
min()函数则相反;它选择最小值。看一下下面的最小函数:
>>> min(230, 812, 109)
这是输出:
109
如你所见,该函数使用了与最大函数相同的列表,但这次输出是109,这是该组数字的最小值。
还有其他数学函数,比如sum()。如果你是 Python 初学者,建议你尝试使用这些函数来了解它们的工作方式。这些函数将成为你算法的关键,因为你设计计算思维问题的解决方案。当我们研究其他 Python 功能时,比如字典和数组,我们也会使用其中一些函数。
使用字典和列表
在深入研究字典和列表之前,重要的是要注意 Python 的内置函数中不包含数组。我们可以使用列表,并对列表执行许多传统的数组函数。但是,对于数组的更强大功能,需要使用库,例如 NumPy。
Python 有四种集合数据类型,如下所示:
-
列表:有序且可更改;可以有重复项
-
元组:有序且不可更改;可以有重复项
-
集合:无序且无索引;不能有重复项
-
字典:无序、可更改和有索引;不能有重复项
如前所述,我们暂时不会涉及 NumPy 库或其他数据库。现在,我们将专注于字典和列表。
定义和使用字典
你可能还记得我们在第三章中使用了字典,理解算法和算法思维,当时我们创建了一个项目菜单。Python 中的字典是具有以下三个特征的集合:
-
它们是无序的。
-
它们是可更改的。
-
它们由键索引。
字典是以值对的形式组织的。例如,我们可以有一个包含州和它们的首府值对的字典。看一下下面的字典:
ch8_dictionary1.py
states = {
'Ohio':'Columbus',
'Delaware':'Dover',
'Pennsylvania':'Harrisburg',
'Vermont':'Montpelier'
}
print(states)
如果我们在没有条件的情况下将这个字典打印出来,我们会得到以下输出:
{'Ohio': 'Columbus', 'Delaware': 'Dover', 'Pennsylvania': 'Harrisburg', 'Vermont': 'Montpelier'}
正如你所看到的,每个值对都一次打印出来。就像我们以这种方式构建了字典一样,Python 也有一个内置的dict()函数。可以使用该函数构建相同的字典,如下所示:
ch8_dictionary2.py
states = dict([
('Ohio','Columbus'),
('Delaware','Dover'),
('Pennsylvania','Harrisburg'),
('Vermont','Montpelier')
])
print(states)
你可以看到,这两个例子中字典的构造方式非常相似,语法上有一些变化,比如第一个实例中使用冒号,而第二个实例中使用括号和逗号。然而,print语句产生了相同的结果。
据说字典是键值对,因为第一个项目是键,第二个配对项目是值。因此,对于Ohio和Columbus,Ohio是键,而Columbus是值。我们可以使用键来调用任何值。例如,我可以调用states['Ohio'],它应该返回'Columbus'。看一下代码:
>>> states['Ohio']
这是输出:
'Columbus'
我们可以为字典中的任何键值对做到这一点。但是,如果我们尝试调用字典中没有的键,比如Alabama,那么我们会得到以下错误:
>>> states['Alabama']
这导致显示以下错误:
Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
states['Alabama']
KeyError: 'Alabama'
请注意,这会导致KeyError,在第七章的错误列表中提到,在解决方案中识别挑战,在逻辑错误部分。但是假设我们确实想要添加Alabama和首都Montgomery。我们可以使用以下代码来实现:
>>> states['Alabama'] = 'Montgomery'
我们可以在输入以下代码后再次调用字典:
>>> print(states)
这给了我们以下输出:
{'Ohio': 'Columbus', 'Delaware': 'Dover', 'Pennsylvania': 'Harrisburg', 'Vermont': 'Montpelier', 'Alabama': 'Montgomery'}
请注意,字典在字典末尾添加了'Alabama':'Montgomery'键值对。
我们还可以使用del代码删除键值对,如下所示:
>>> del states['Delaware']
现在,如果我们继续打印states字典,Delaware就不再在列表中了:
>>> print(states)
删除州之后的输出:
{'Ohio': 'Columbus', 'Pennsylvania': 'Harrisburg', 'Vermont': 'Montpelier', 'Alabama': 'Montgomery'}
现在,您可以继续添加项目或删除它们,而无需进入主算法。
使用字典,您还可以向一个键添加多个值。假设我玩三种运动(我没有)。我可以在字典中列出它们,并将它们与一个键值匹配。看一下以下算法:
ch8_dictionary3.py
miscellaneous = {
'sports' : ['tennis', 'bowling', 'golf'],
'age' : '40',
'home' : 'lake'
}
我们可以使用print(miscellaneous)打印完整的字典,如下所示:
>>> print(miscellaneous)
这就是我们得到输出的方式:
{'sports': ['tennis', 'bowling', 'golf'], 'age': '40', 'home': 'lake'}
如您所见,打印的字典包括所有值。如果我只想打印运动项目,那么我可以使用miscellaneous['sports']代码:
>>> miscellaneous['sports']
这是输出:
['tennis', 'bowling', 'golf']
请注意,我们根本没有使用print()函数。我们使用了我们称之为miscellaneous的字典,并调用了'sports'键。请注意,我们之所以能够得到这个结果,是因为我们在 Python 的IDLE命令窗口中调用了字典。如果您想将其包含在您的算法中,您仍然需要使用print()函数。
虽然我们在这里不会详细介绍字典的所有功能,但您可以看到它们如何有助于创建包含键值对的算法。Python 允许我们添加和修改字典,调用值等,而无需访问整个字典来实现这一点。
接下来,让我们来看看列表。
定义和使用列表
Python 中的列表是有序且可更改的。我们可以为任何事物创建列表,比如动物的类型、颜色、水果、数字,或者真的任何我们想要的东西。由于我们可以有重复的值,我们可以有一个只说apple, apple, apple的三个苹果的列表。例如,看一下所示的列表:
ch8_list1.py
fruits = ['apple','apple','apple']
print(fruits)
当我们打印这个列表时,所有项目都包括在内。输出如下:
['apple', 'apple', 'apple']
正如您所看到的,我们有相同的值三次。我们无法在字典中做到这一点,因为字典不允许重复成员。
现在,让我们看看我们可以用列表做什么。让我们从以下动物列表开始:
ch8_list2.py
animals = ['dog', 'cat', 'bird', 'lion', 'tiger', 'elephant']
列表中的第一项是'dog'。列表有索引,从 0 开始。因此,如果我们打印animals[0],我们会得到dog。我们可以在这里检查:
>>> print(animals[0])
这是索引 0 的输出:
dog
列表中有六个项目,但最后一个索引是[5]。因此,要打印elephant,我们需要使用该索引进行打印:
>>> print(animals[5])
elephant
您还可以在列表中使用负数索引。[–1]索引是指最后一个索引,因此它代表elephant,与索引[5]相同:
>>> print(animals[-1])
elephant
[–2]索引是指倒数第二个索引,因此是tiger,以此类推。
我们还可以通过指定索引范围来打印列表中的多个项目。看一下以下代码:
>>> print(animals[1:4])
这是输出:
['cat', 'bird', 'lion']
如你所见,我们打印了列表中的第二个项目,对应索引[1],以及接下来的两个项目。
使用列表,我们还可以添加项目,替换项目,删除项目,检查长度等等。要添加一个项目,我们需要使用append()方法。让我们把duck项目添加到我们的列表中:
>>> animals.append('duck')
>>> print(animals)
这是添加的项目:
['dog', 'cat', 'bird', 'lion', 'tiger', 'elephant', 'duck']
请注意,我们的列表现在在列表末尾有duck。但是假设我们想要移除bird并用butterfly替换它:
首先,我们必须确定bird的索引。该索引是2,因为它是列表中的第三个项目:
>>> animals[2] = 'butterfly'
>>> print(animals)
这是输出,bird被替换了:
['dog', 'cat', 'butterfly', 'lion', 'tiger', 'elephant', 'duck']
列表现在包含butterfly。
删除一个项目相当简单,我们只需使用remove()方法和我们想要删除的项目:
>>> animals.remove('lion')
>>> print(animals)
如你所见,lion已经从列表中移除了:
['dog', 'cat', 'butterfly', 'tiger', 'elephant', 'duck']
我们还可以通过索引使用pop()方法删除一个项目。使用索引1会移除列表中的第二个项目。参考以下代码:
>>> animals.pop(1)
'cat'
然后,我们可以尝试以下操作:
>>> print(animals)
['dog', 'butterfly', 'tiger', 'elephant', 'duck']
请注意,Python 识别了从列表中弹出的索引1处的项目。当我们再次打印列表时,该项目就不再存在了。如果我们想要移除最后一个项目,我们不必指定索引。参考以下代码:
>>> animals.pop()
'duck'
然后,我们可以尝试以下操作:
>>> print(animals)
['dog', 'butterfly', 'tiger', 'elephant']
如前所述,当没有指定索引时,列表上的最后一个项目会被弹出并从列表中移除。
还有一种方法可以从列表中删除一个项目,那就是使用del关键字:
>>> del animals[1]
>>> print(animals)
这是输出:
['dog', 'tiger', 'elephant']
我们的列表失去了列表中的第二个项目。我们还可以使用del关键字完全删除列表:
>>> del animals
>>> print(animals)
这个错误是输出在这里:
Traceback (most recent call last):
File "<pyshell#49>", line 1, in <module>
print(animals)
NameError: name 'animals' is not defined
如你所见,我不能再打印列表,因为它没有定义。请注意,我们收到了一个NameError描述name 'animals' is not defined。这是第七章中提到的另一个错误,在解决方案中识别挑战。
重要提示:
在接下来的几个代码示例中,我再次运行了ch8_list2.py文件中的原始代码,以获得我的原始列表。
我们可以使用clear()方法清空整个列表,而不是消除实际的列表。
>>> animals.clear()
>>> print(animals)
这是我们得到的输出:
[]
在这种情况下,列表现在打印为空列表,而不会给出错误消息。这是因为列表仍然存在并且已定义,只是空的。
现在,假设我想要知道我的列表的长度。我们可以使用len()来找到列表的长度。同样,我回到原始列表运行以下代码:
>>> print(len(animals))
我们得到以下输出:
6
我们的原始列表包含六个元素 - 也就是六种动物。
现在,让我们定义另一个包含颜色的列表:
ch8_list3.py
animals = ['dog', 'cat', 'bird', 'lion', 'tiger', 'elephant']
colors = ['blue', 'red', 'yellow']
print(animals)
print(colors)
这个算法的输出是两个列表。现在,如果我们想要合并这两个列表,我们可以使用extend()方法。
让我们看看如何将colors附加到animals列表中:
>>> animals.extend(colors)
>>> print(animals)
这是追加后的列表:
['dog', 'cat', 'bird', 'lion', 'tiger', 'elephant', 'blue', 'red', 'yellow']
我们的列表现在包含了所有的动物和所有的颜色。我们也可以使用以下方法将colors与animals扩展。不同之处在于颜色会首先出现在我们的列表中:
>>> colors.extend(animals)
>>> print(colors)
现在列表的样子是这样的:
['blue', 'red', 'yellow', 'dog', 'cat', 'bird', 'lion', 'tiger', 'elephant']
我们还可以对列表进行排序,当我们想要有一个按字母顺序排列的列表或者想要对数字列表进行排序时,这是很有帮助的。看一下以下算法中的两个列表:
ch8_list4.py
animals = ['dog', 'cat', 'bird', 'lion', 'tiger', 'elephant']
numbers = [4, 1, 5, 8, 2, 4]
print(animals)
print(numbers)
这是它们未排序时的样子:
['dog', 'cat', 'bird', 'lion', 'tiger', 'elephant']
[4, 1, 5, 8, 2, 4]
让我们对两个列表进行排序,看看会发生什么:
>>> numbers.sort()
>>> print(numbers)
我们将得到以下输出:
[1, 2, 4, 4, 5, 8]
然后,我们可以尝试以下操作:
>>> animals.sort()
>>> print(animals)
我们得到了这个输出:
['bird', 'cat', 'dog', 'elephant', 'lion', 'tiger']
正如你所看到的,数字按从小到大的顺序排序,而动物按字母顺序排序。
让我们谈一下为什么这样很有帮助。想象一下,你的网站上显示了来自 Python 列表的项目列表。它们都被排序并完美地显示出来。但现在让我们说你想添加更多的项目。使用我们讨论过的方法,将它们以任何顺序添加到你的列表中会更容易,然后我们可以对它们进行排序,以便它们继续按字母顺序排列。
当然,这些不是我们可以用列表做的唯一的事情。Python 允许我们以许多用户友好的方式使用列表,并且与其他编程语言中的数组类似地进行操作。当我们需要以其他方式使用它们时,我们可以搜索包含这些功能的库。
在 Python 编程语言中,列表和字典都很重要。在本节中,你看到字典使用键值对,而列表包括值。两者都可以使用内置在 Python 编程语言中的方法和函数进行编辑。当我们将 Python 应用于更复杂的计算思维问题时,你将再次看到它们,这在本书的第三部分,使用计算思维和 Python 进行数据处理、分析和应用中。
现在,让我们看看如何在 Python 中使用变量和函数。
使用变量和函数
在 Python 中,我们使用变量来存储一个值。然后我们可以使用这个值来执行操作,评估表达式,或者在函数中使用它们。函数在算法中被调用时给出一系列指令。许多函数在其中包括变量。所以,让我们首先看看如何定义和使用变量,然后再看看 Python 函数。
Python 中的变量
Python 没有声明变量的命令。我们可以通过给它们命名并将它们设置为我们想要的任何值来创建变量。让我们看一个包含多个变量的算法:
ch8_variables.py
name = 'Marcus'
b = 10
country_1 = 'Greece'
print(name)
print(b)
print(country_1)
正如你所看到的,我们可以使用字母、更长的名称,甚至在变量命名中包括下划线。但是,我们不能以数字开头命名变量。当我们运行这个程序时,我们得到以下输出:
Marcus
10
Greece
每个变量都被打印出来,没有任何问题。如果我们使用数字来开始任何变量名,我们会得到一个错误。但是,如果我将country_1变量命名为_country,那将是 Python 中一个可接受的变量名。
现在,让我们看看我们可以用变量做什么。
组合变量
变量允许我们做的一件事是在print语句中组合它们。例如,我可以创建一个打印Marcus Greece的print语句。为了做到这一点,我可以使用+字符,如下所示:
>>> print(name + ' ' + country_1)
请注意,在两个+字符之间,有' '。这是为了添加一个空格,这样我们的print语句就不会看起来像MarcusGreece。现在,让我们在print语句中组合b和name:
>>> print(b + name)
这将给我们一个错误消息:
Traceback (most recent call last):
File "<pyshell#70>", line 1, in <module>
print(b + name)
TypeError: unsupported operand type(s) for +: 'int' and 'str'
请注意,错误说明了TypeError: unsupported operand types for +。它还说明了我们有'int'和'str',这意味着我们正在组合两种不同的数据类型。
为了能够在print语句中组合这两种数据类型,我们可以将int(我们的b变量)转换为str。看起来是这样的:
>>> print(str(b) + ' ' + name)
这很容易给我们想要的结果:
10 Marcus
现在我们已经将它们都变成了字符串,我们可以在print函数中将它们组合起来。
有时,我们会想一次创建多个变量。Python 允许我们用一行代码来做到这一点:
ch8_variables2.py
a, b, c, d = 'John', 'Mike', 'Jayden', 'George'
print(a)
print(b)
print(c)
print(d)
当我们运行这个算法时,我们得到以下输出:
John
Mike
Jayden
George
正如你所看到的,当我们打印a变量时,等号右边的第一个值被打印出来。算法的第一行分配了四个不同的值给四个不同的变量。
现在让我们看看函数,因为我们需要它们来进一步讨论变量。
使用函数
在第七章,在解决方案中识别挑战中,我们编写了一个算法,打印出任何给定数字范围内的偶数。我们将通过定义一个函数来重新访问这个问题。让我们看一下偶数问题的算法:
ch8_evenNumbers.py
def evenNumbers(i, j):
a = i - 1
b = j + 1
for number in range(a, b):
if number % 2 == 0:
print(number)
a = a + 1
如果我们在 Python 中运行此程序,将不会有输出;但是,我们现在可以为任何数字范围运行该函数。请注意,我们在此函数中添加了a和b变量。这样在程序运行时就会包括端点。
让我们看看当我们运行范围为(2, 10)和(12, 25)时会发生什么:
>>> evenNumbers(2, 10)
输出如下:
2
4
6
8
10
然后,我们可以尝试以下操作:
>>> evenNumbers(12, 25)
输出如下:
12
14
16
18
20
22
24
正如您所看到的,我们无需进入算法来调用函数,一旦我们运行了函数,我们就可以在 Python shell 中为任何范围调用它。与以前一样,如果我们的范围太大,shell 将显示一个非常长的数字列表。因此,我们可以定义另一个变量,这次是一个列表,并在函数内部将值附加到该列表:
ch8_evenNumbers2.py
listEvens = []
def evenNumbers(i, j):
a = i - 1
b = j + 1
for number in range(a, b):
if number % 2 == 0:
listEvens.append(number)
a = a + 1
print(listEvens)
请注意,我们在函数外定义了列表。我们可以以任何方式做。它可以存在于函数内部或外部。区别在于,即使未调用函数,列表也存在;也就是说,一旦运行算法,我可以调用列表,它将为空,或者调用函数,它将使用列表。如果在函数外部,它是全局变量。如果在内部,只有在调用函数时才存在。
让我们现在尝试运行范围为(10, 50)的程序:
>>> evenNumbers(10, 50)
这是输出:
[10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50]
正如您所看到的,10和50之间的偶数现在包括在列表中并打印出来。
让我们看另一个接受(name)字符串参数的函数的示例:
ch8_nameFunction.py
def nameFunction(name):
print('Hello ' + name)
一旦运行了这个算法,我们就可以为任何名称调用该函数。看一下:
>>> nameFunction('Sofia')
我们得到以下输出:
Hello Sofia
让我们输入另一个名称:
>>> nameFunction('Katya')
我们将看到以下输出:
Hello Katya
正如您所看到的,一旦在算法中定义了一个函数并运行了算法,我们可以根据需要调用该函数。
现在,我们在先前的偶数示例中使用了迭代,所以是时候更仔细地看看文件、数据和迭代了,让我们暂停一下并看看。当我们进行迭代时,我们将遇到更多的函数示例。
学习有关文件、数据和迭代的知识
在本节中,我们将看看如何处理文件、数据和迭代与 Python。这将为我们提供有关如何使用算法来打开、编辑和运行已存在的 Python 文件或新文件的信息。通过迭代,我们将学习如何根据某些条件重复代码行,限制算法的某个部分将运行多少次或在什么条件下运行。我们将首先从文件开始。
在 Python 中处理文件
在 Python 中,与文件相关的主要函数是open()函数。如果要在相同目录中打开文件,可以使用以下命令行:
fOpen = open('filename.txt')
如果文件位于另一个位置,您需要找到文件的路径并在命令中包含该路径,如下所示:
fOpen = open('C:/Documents/filename.txt')
除了打开文件之外,我们还可以告诉程序做一些其他事情。它们列在下面:
-
r:用于打开文件进行读取(这是默认值,因此不需要包含);如果文件不存在,则创建新文件。 -
w:用于打开文件进行写入;如果文件不存在,则创建新文件。 -
a:用于打开文件以进行追加;如果文件不存在,则创建新文件。 -
x:用于创建指定的文件;如果文件已经存在,则返回错误。
除了列出的方法之外,还有两件事可以用于标识文件需要如何处理 - 即作为二进制还是文本:
-
t:文本(这是默认值,因此如果未指定,则默认为文本) -
b: 二进制(用于二进制模式 - 例如图像)
看一下以下代码。由于这段代码是单行的,而且针对每个路径都是特定的,所以没有包含在存储库中:
fOpen = open('filename.txt', 'rt')
前面的代码与先前的代码相同,fOpen = open('filename.txt')。由于r和t是默认值,不包括它们会导致算法的相同执行。
如果我们想要打开一个文件进行写入,我们可以使用fOpen = open('filename.txt', 'w')代码。在 Python 中关闭文件,我们使用close()代码。
文本文件的一些附加方法包括在以下列表中。这不是一个详尽的列表,但包含了一些最常用的方法:
-
read(): 可以用来读取文件的行;使用read(3)读取前 3 个数据。 -
tell(): 用于找到当前位置的字节数。 -
seek(): 将光标移动到原始/初始位置。 -
readline(): 读取文件的每一行。 -
detach(): 用于将底层二进制缓冲区与TextIOBase分离并返回它。 -
readable(): 如果可以读取文件流,则返回True。 -
fileno(): 用于返回文件的整数编号。
正如你所看到的,你可以使用 Python 来操纵并从文本文件中获取信息。例如,当向现有文本文件添加代码行时,这可能会很有用。现在,让我们来看看 Python 中的数据。
Python 中的数据
在我们开始处理数据之前,让我们澄清一下,我们不是在讨论数据类型,这是我们在第一章中讨论的,计算机科学基础。在本章中,我们将主要以列表的形式查看数据和与数据交互的方式。
让我们看看我们可以用数据做些什么。
首先,从存储库中获取ch8_survey.txt文件。你将需要它用于下一个算法。
假设你让你的朋友们在蓝色、红色和黄色之间为一个团体标志进行选择。ch8_survey.txt文件包含了这些投票的结果。Python 允许我们操纵这些数据。例如,文件的第一行说Payton – Blue。我们可以使该行打印出Payton 投票支持蓝色。让我们看看算法:
ch8_surveyData.py
with open("ch8_survey.txt") as file:
for line in file:
line = line.strip()
divide = line.split(" - ")
name = divide[0]
color = divide[1]
print(name + " voted for " + color)
让我们分解我们的代码,以了解发生了什么:
-
第一行打开了调查文件并保持打开状态。
-
下一行,
for line in file,将遍历文件中的每一行执行以下命令。 -
然后算法将信息分割在破折号处。第一部分被定义为名字(
name = divide[0]),第二部分被定义为颜色(color = divide[1])。 -
最后,代码打印出了带有
投票支持文本的行。
看一下以下输出:
Payton voted for Blue
Johnny voted for Red
Maxi voted for Blue
Jacky voted for Red
Alicia voted for Blue
Jackson voted for Yellow
Percy voted for Yellow
正如你所看到的,现在每一行都经过调整,去掉了破折号,并包括了投票支持短语。但是如果你想要计算投票呢?我们也可以为此编写一个算法:
ch8_surveyData2.py
print("The votes for Blue are in.")
blues = 0
with open("ch8_survey.txt") as file:
for line in file:
line = line.strip()
name, color = line.split(' - ')
if color == "Blue":
blues = blues + 1
print(blues)
正如你所看到的,我们正在通过验证每一行并使用if color == "Blue"代码来计算投票。当我们运行这个算法时,我们得到以下输出:
The votes for Blue are in.
3
正如你所看到的,该算法打印出了初始的print()命令,然后打印出了蓝色的计数投票。
我们也可以处理数据,找出诸如均值、中位数和众数之类的东西,但现在我们不会详细讨论。如果将来我们在某个应用问题中需要它们,我们会使用它们。然而,大多数这些特定于数据的问题将使用库来简化一些算法和计算。
现在,让我们再谈一下迭代,我们一直在使用,但需要进一步定义以更好地理解其用法。
在算法中使用迭代
在我们进入迭代之前,让我们定义一下术语。迭代意味着重复。当我们在算法中使用迭代时,我们正在重复步骤。想想我们之前在问题中使用过的for循环,比如偶数问题——算法迭代了一系列数字。它对我们范围内的所有数字进行了重复。
让我们看另一个例子:
ch8_colorLoop.py
colors = ['blue', 'green', 'red']
for color in colors:
print(color)
该算法中的迭代是它将为原始颜色列表中的每种颜色重复执行print过程。
这是输出:
blue
green
red
如你所见,每种颜色都被打印出来了。
我们还可以从用户那里获取输入,然后迭代执行一些操作。例如,看看以下算法:
ch8_whileAlgorithm.py
ask = int(input("Please type a number less than 20\. "))
while ask > 0:
print(ask * 2)
ask = ask – 1
如你所见,我们将输入变量定义为ask。然后,只要ask大于0,我们就打印出加倍的数字并将数字减1。
输出如下:
Please type a number less than 20\. 8
16
14
12
10
8
6
4
2
输出显示了通过将初始数字加倍而创建的列表,即8 x 2 = 16,然后继续直到ask变量不再大于0。
接下来,让我们看看如何可以迭代遍历多个列表。该算法使用两个列表,然后使用两者的信息打印出一个语句:
ch8_Iterations.py
jewelry = ['ring', 'watch', 'necklace', 'earrings', 'bracelets']
colors = ['gold', 'silver', 'blue', 'red', 'black']
for j, c in zip(jewelry, colors):
print("Type of jewelry: %s in %s color. " %(j, c))
当我们运行算法时,我们得到以下输出:
Type of jewelry: ring in gold color.
Type of jewelry: watch in silver color.
Type of jewelry: necklace in blue color.
Type of jewelry: earrings in red color.
Type of jewelry: bracelets in black color.
看一下print语句:print("Type of jewelry: %s in %s color." %(j, c))。%s符号将分别被(j, c)的值替换。因此,第一个%s符号获取j的项目,第二个%s符号获取c的项目,其中j是来自珠宝列表的项目,c是来自颜色列表的颜色。
如你所见,我们可以以多种方式迭代列表。这只是一些示例,让我们熟悉循环和如何在我们的算法中使用信息。随着我们深入更复杂的问题,算法将变得更复杂,所以我们将重新讨论本章和之前章节中的许多主题。
在进入下一章之前,我们需要看一下面向对象编程(OOP)。
使用面向对象编程
面向对象编程是一种将数据结构化为对象的方法。Python 程序是一种面向对象的程序,它将算法结构化为对象,以便根据属性和行为对它们进行打包。要了解面向对象编程,我们需要知道如何做以下事情:
-
创建类
-
使用类创建对象
-
使用类继承来建模系统
在 Python 中,我们使用类来打包数据。要理解类,让我们创建一个:
>>> class Books:
pass
然后我们可以调用Books()类并获取类在计算机上保存的位置。请注意,我的输出将与您的不同,因为类将保存在不同的位置:
>>> Books()
这是我的输出:
<__main__.Books object at 0x000001DD27E09DD8>
现在我们已经创建了类,我们可以向类添加书籍对象:
>>> a = Books()
>>> b = Books()
这些实例中的每一个都是Books()中的一个独特对象。如果我们要比较它们,由于它们是不同的,a == b将返回False。
现在,让我们看一个创建地址簿的类。通过创建类,我们可以根据需要添加条目:
ch8_addressBook.py
class Entry:
def __init__(self, firstName, lastName, phone):
self.firstName = firstName
self.lastName = lastName
self.phone = phone
在这个算法中,我们创建了一个代表地址簿中条目的Entry类。一旦我们运行算法,我们就可以向地址簿添加条目并调用它们的信息。例如,看看以下代码:
>>> Johnny = Entry('John', 'Smith', '201-444-5555')
这段代码将Johnny输入地址簿。现在,我们可以分别调用 Johnny 的名字、姓氏和电话号码,如下所示:
>>> Johnny.firstName
这是我的输出:
'John'
我们可以调用姓氏:
>>> Johnny.lastName
这是获得的输出:
'Smith'
我们还可以调用电话号码:
>>> Johnny.phone
我们可以看到这个输出:
'201-444-5555'
我们可以添加任意多的条目,然后根据需要调用它们:
>>> max = Entry('Max', 'Minnow', '555-555-5555')
>>> emma = Entry('Emma', 'Dixon', '211-999-9999')
>>> emma.phone
这是我们的输出:
'211-999-9999'
添加条目后,我们可以调用姓氏:
>>> max.lastName
我们得到这个输出:
'Minnow'
如你所见,我们添加了两个新条目,然后调用了 Emma 的电话号码和 Max 的姓氏。
一旦我们有了类,我们就可以有一个类继承其他类的方法和属性。因为这些类继承了属性和方法,原始类被称为父类,而新类被称为子类。
回到我们的地址簿,我们可以通过将类传递给新类来创建一个子类。我知道,这听起来很混乱,但看一下这个算法:
>>> class Job(Entry):
pass
现在我们也可以使用子类添加更多项目。看下面的例子:
>>> engineer = Job('Justin', 'Jackson', '444-444-4444')
我们可以用类做更多的事情,但让我们尝试通过解决一个问题并设计一个算法,使用至少学到的一些组件来总结这一章。
问题 1 - 创建一个书库
假设你有很多书,想创建一个存储有关书籍信息的算法。你想记录每本书的标题、作者、出版日期和页数。为这种情况创建一个算法。
首先,让我们思考一下问题:
-
我拥有的书的数量不断变化,所以我希望创建一个可以根据需要添加信息的东西。
-
我也希望能够删除我不再拥有的书。
虽然我们可以使用库,但对于这个特定的问题,最好的解决方案是一个类。让我们从创建一个名为Books的类开始:
ch8_BookLibrary.py
class Books:
def __init__(self, title, author, pubDate, pages):
self.title = title
self.author = author
self.pubDate = pubDate
self.pages = pages
book1 = Books('The Fundamentals of Fashion Design', 'Sorger & Udale', '2006', '176')
book2 = Books('Projekt 1065: A Novel of World War II', 'Gratz', '2016', '309')
如您所见,我们已经定义了我们的类。该类有四个参数:title、author、pubDate和pages。在定义之后,添加了两本书。当我们运行这个程序时,实际上什么都不会发生,但我们可以调用任一本书的信息:
>>> book1.title
我们将得到以下输出:
'The Fundamentals of Fashion Design'
我们将调用book2的出版日期:
>>> book2.pubDate
我们得到这个输出:
'2016'
您可以看到,我现在可以在运行算法后调用每本书保存的任何元素。
现在,让我们看看如何在 Python shell 中添加第三本书:
>>> book3 = Books('peanut butter dogs', 'Murray', '2017', '160')
由于书已经添加,我们也可以调用关于这本书的信息:
>>> book3.title
我们得到这个输出:
'peanut butter dogs'
我们可以调用book3的总页数:
>>> book3.pages
我们将得到以下输出:
'160'
如您所见,我们可以在算法中添加书籍到我们的类中,或者在 Python shell 中运行算法后添加书籍。现在,让我们看看另一个问题。
问题 2 - 组织信息
我们被要求创建一个算法,它接受三个数字作为输入,并提供这些数字的总和。我们可以用多种方法来做到这一点,但让我们看看如何使用eval()函数:
ch8_Sums.py
a = int(input("Provide the first number to be added. "))
b = int(input("Please provide the second number to be added. "))
c = int(input("Provide the last number to be added. "))
print(eval('a + b + c'))
请注意,我们将每个输入变量定义为int类型。这样定义是为了正确执行评估。
这是我们算法的输出:
Provide the first number to be added. 1
Please provide the second number to be added. 2
Provide the last number to be added. 3
6
如果我们忘记为每个数字添加类型,函数会将其评估为123,因为它只是将每个字符串添加到下一个字符串。所以,如果我们的输入是John、Mary、Jack,我们的输出将是JohnMaryJack。
我们之前没有涉及sum()函数。让我们看看如何使用该函数:
ch8_Sums2.py
a = int(input("Provide the first number to be added. "))
b = int(input("Please provide the second number to be added. "))
c = int(input("Provide the last number to be added. "))
numbers = []
numbers.append(a)
numbers.append(b)
numbers.append(c)
print(sum(numbers))
在这种情况下使用sum需要我们将输入添加到列表中,因为sum()可以处理可迭代对象,比如列表。虽然这种解决方案代码更多,但输出与我们的eval()函数完全相同,如下所示:
Provide the first number to be added. 1
Please provide the second number to be added. 2
Provide the last number to be added. 3
6
如您所见,我们使用不同的 Python 函数得到了与之前相同的答案。在我们继续下一章之前,让我们再看一个问题。
问题 3 - 循环和数学
对于这个问题,我们必须创建一个算法,打印出给定范围内所有数字的平方。记住,我们可以单独打印每个数字,但如果范围很大,最好我们有一个列表。我们还需要在范围内进行迭代,并且如果我们想要包括端点,我们必须将最大值加 1。
看一下以下算法:
ch8_SquareLoops.py
print("This program will print the squares of the numbers in a given range of numbers.")
a = int(input("What is the minimum of your range? "))
b = int(input("What is the maximum of your range? "))
Numbers = []
b = b + 1
for i in range(a, b):
j = i**2
Numbers.append(j)
i = i + 1
print(Numbers)
注意我们在算法中添加了一个j变量。我们没有使用i = i**2,因为那样会改变i的值,这会影响算法中的迭代。通过使用j,我们可以使用i来遍历给定的范围。让我们来看看我们的输出:
This program will print the squares of the numbers in a given range of numbers.
What is the minimum of your range? 4
What is the maximum of your range? 14
[16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196]
该算法打印出我们提供范围的平方列表。它还有一个初始的print语句,解释了代码将要做的事情。
现在我们已经看了一些例子,让我们来看看父类和子类以及继承是如何工作的。
使用继承
在 Python 中,我们可以使用继承将方法和属性从一个类传递到另一个类。父类具有将被继承的方法和属性。子类继承自父类。父类是一个对象,子类也是一个对象,所以我们正在定义类的属性。
让我们看看如何使用一个科学例子。我们将创建一个名为mammals的类。并不是所有的哺乳动物都是胎生的。哺乳动物在产下活仔时是胎生的。不是胎生的哺乳动物是鸭嘴兽。鸭嘴兽产卵而不是产下活仔。如果我们要为此编写一个算法,我们希望动物继承父类的特征 - 在这种情况下是mammals。让我们看看下面的代码片段:
ch8_mammals.py
class mammals:
def description(self):
print("Mammals are vertebrate animals.")
def viviparous(self):
print("Mammals are viviparous, but some are not.")
class monkey(mammals):
def viviparous(self):
print("Monkeys are viviparous.")
class platypus(mammals):
def viviparous(self):
print("The platypus is not viviparous. It's an egg-laying mammal.")
obj_mammals = mammals()
obj_monkey = monkey()
obj_platypus = platypus()
obj_mammals.description()
obj_mammals.viviparous()
obj_monkey.description()
obj_monkey.viviparous()
obj_platypus.description()
obj_platypus.viviparous()
从前面的代码中,注意到mammals()类使用了一个描述,然后是有关哺乳动物和胎生的信息。monkey使用了与mammals类相同的描述,但是包括了一个不同的胎生声明。platypus也是一样的情况。monkey和platypus类都是mammals类的子类。
这三个类,父类和两个子类,然后被简化为一个变量,以便通过调用该变量来使用。最后,算法打印出了父类和两个子类的描述和胎生声明。让我们来看看输出:
Mammals are vertebrate animals.
Mammals are viviparous, but some are not.
Mammals are vertebrate animals.
Monkeys are viviparous.
Mammals are vertebrate animals.
The platypus is not viviparous. It's an egg-laying mammal.
正如你所看到的,所有三个类都使用了相同的描述。这是因为我们没有对子类的描述进行任何更改。当我们定义类时,我们只改变了想要与父类不同的部分。其他所有内容都是从父类继承而来的。
在 Python 中,父类和子类被广泛用于多种目的。例如,在游戏中,你可能会有一些具有相同特征的敌人。我们可以创建一个包含所有共同特征的父类,然后将所有敌人的个体特征作为子类进行更改,而不是分别定义每个敌人的所有特征。这只是继承可以使用的方式之一。还有许多其他用途,它可以帮助我们节省时间,并通过只用父类定义它们来避免错误。
现在我们已经有了一些类的经验,并学习了面向对象编程,让我们结束本章。
摘要
在本章中,我们讨论了 Python 编程的基础知识。我们研究了一些内置函数,使用了字典和列表,使用了变量和函数,学习了有关文件、数据和迭代的知识,并学习了类和面向对象编程。
正如我们在本章和解决以前的问题时提到的,Python 为我们提供了多种解决同一问题的方法。其中一个例子就是在本章的“问题 2 - 组织信息”部分提供的,我们在两种不同的算法中使用了eval()和sum()函数来产生相同的结果。随着我们继续学习 Python 以及如何编写我们的算法,选择使用哪些函数、变量、参数等将开始变得自然而然。本章中一些更具挑战性的概念和内容涉及数据分析,比如我们在介绍 Python 中的数据时使用的调查,以及类。在这些领域进行一些练习是很重要的,这样你就能更好地理解这些主题。
在这一章之后,你现在可以使用内置函数,区分列表、字典和类,并解决结合多个不同概念的问题,比如变量、迭代和列表。在下一章中,我们将更深入地研究输入和输出,以帮助我们设计算法。
第九章:理解输入和输出以设计解决方案算法
在本章中,我们将深入研究问题,以确定设计问题算法所需的输入和输出。我们将使用我们在第八章中学到的概念,Python 简介,在那里我们讨论了面向对象的编程、字典、列表等。当您练习获取输入并在算法中使用它时,您将能够看到算法的输出取决于输入信息。
在本章中,我们将涵盖以下主题:
-
定义输入和输出
-
理解计算思维中的输入和输出
在本章中,我们将重点关注理解不同类型的输入以及如何使用Python编程语言中的输出。为了更好地理解这些主题,我们首先要看一下它们的定义。
技术要求
对于本章,您需要安装最新的 Python 版本。
您可以在此章节中找到使用的源代码:github.com/PacktPublishing/Applied-Computational-Thinking-with-Python/tree/master/Chapter09
定义输入和输出
我们将从研究输入及其定义开始,这反过来又用于提供结果或输出。
输入是放入系统或机器中的东西。在计算机中,我们有输入设备,计算机会解释这些设备以提供结果或输出。当我们看不同类型的输入以及如何在算法中使用输入时,您将看到我们从这些算法中获得的输出。让我们看一些输入设备的例子:
-
键盘:当我们使用键盘时,计算机会解释按键,并在各种程序中使用该解释,比如文档编写和搜索栏。
-
鼠标:鼠标用于在我们的物理屏幕上导航,帮助我们点击内容和滚动页面。
-
操纵杆:操纵杆可用于玩电脑游戏。
-
麦克风:在更现代的机器中,麦克风不仅用于通过电话和视频应用进行通信,还用于接受人工智能(AI)助手如Cortana和Siri的口头命令,以及语音转文字命令。
所有上述输入都被编程到我们的计算机中以便使用。在 Python 中,我们编写使用这些输入的算法。用户输入是指程序询问用户将用于生成输出的信息。请记住,输入和输出可以在算法中的任何时间发生。我们还可以提供需要用户额外输入的输出反馈,依此类推。让我们看看如何从 Python 获取用户输入。
Python 有一个主要的用户提示,即input()函数。此函数用于接受用户输入,然后自动将输入转换为用户期望的输出。
我们之前在多个程序中使用过这些主要提示,比如第五章中的商店示例,探索问题分析。让我们看看算法中一些输入命令的样子。看看以下代码片段:
ch9_input1.py
name = input("What is your name? ")
print("Nice to meet you " + name + ".")
片段要求用户输入他们的名字,然后使用输入的信息打印一个声明。这段代码的结果如下:
What is your name? Mikayla
Nice to meet you Mikayla.
现在,我们可以只要求输入而不使用任何提示问题,但用户将不知道正在被问及什么或者它将如何被使用。看看没有提示的片段,如下所示:
ch9_input2.py
name = input()
print("Nice to meet you " + name + ".")
当我们运行上述程序时,在我们的 shell 中没有打印任何内容。然而,我知道我需要输入一些东西,所以看看当我只输入一个字母时发生了什么:
d
Nice to meet you d.
正如你所看到的,用户不会知道该怎么做,因为窗口没有询问任何事情;它只是给了一个空白的空间,用户必须假设那是输入的地方。但这可能会导致很多混乱,因为没有告诉用户是否需要输入,或者他们需要输入什么样的东西。
我们可以通过使用 print 语句来减轻一些混乱,例如,通过向用户提供关于我们将要询问的输入的信息。我们还可以在 input() 函数中包含一条语句。例如,我们可以使用这样的一行代码 name = input('你叫什么名字?'),这样首先会显示带引号的语句,这样用户就知道我们在问什么。无论哪种方式,提供一条语句让用户知道需要什么是我们设计算法时非常重要的。
正如你在前面的例子中看到的,input() 函数接受一个字符串,然后在 print() 语句中使用该字符串。让我们看看使用 input 命令时列表会是什么样子:
ch9_input3.py
#Create the list
names = []
#Ask user how many names will be added
name = int(input("How many names will be in the list? "))
#Iterate to add each name to the list
for i in range(0, name):
people = str(input())
names.append(people)
print(names)
在上面的片段中,我们首先创建了列表,这不会显示给用户。然后询问用户将有多少个名字添加到列表中。之后,我们遍历列表,以便用户可以分别输入每个名字。最后,名字被添加到列表中并打印出来。看一下这个算法的输出:
How many names will be in the list? 4
Manny
Lisa
John
Katya
['Manny', 'Lisa', 'John', 'Katya']
注意,名字没有提示,所以算法假设用户在输入值 4 后会知道该怎么做。这可以通过在迭代中简单添加来减轻。看一下下面的代码片段:
ch9_input4.py
#Create the list
names = []
#Ask user how many names will be added
name = int(input("How many names will be in the list? "))
#Iterate to add each name to the list
for i in range(0, name):
people = input("Type the next name on the list. ")
names.append(people)
print(names)
正如你所看到的,每个名字现在都有一个提示,要求输入下一个名字,当算法运行时,输出看起来像下面的样子:
How many names will be in the list? 4
Type the next name on the list. Milo
Type the next name on the list. Sonya
Type the next name on the list. Gabriel
Type the next name on the list. Maxine
['Milo', 'Sonya', 'Gabriel', 'Maxine']
上面的输出显示了完成的列表以及添加到该列表中的每个名字的提示。在算法中对这些提示进行简单的添加可以减轻用户在输入时的混乱。
正如你从本节中前面的片段中看到的,输入被读取为整数、字符串和列表。Python 自动转换了信息,以便算法可以使用它。
在 Python 中,还有一种方法可以使用一行代码从多个输入中定义多个变量。看一下下面的代码片段:
ch9_input5.py
name1, name2 = input("Enter First Name: "), input("Enter Last Name: ")
print(name1 + " " + name2)
正如你在前面的代码中所看到的,print() 命令中的引号(" ") 用于分隔输入。看一下这个算法的输出:
Enter First Name: John
Enter Last Name: Doe
John Doe
正如你所看到的,程序要求输入名字,这些名字在算法的第一行中被调用。这绝不是我们从用户那里获取输入的唯一方式,也不是我们将使用的唯一输入。
在本节中,我们看了一些在算法中定义输入的方法。我们还学会了如何从用户那里获取输入以便稍后在算法中使用。现在让我们转到下一节,我们将在其中看一些计算思维问题中的输入和输出。
理解计算思维中的输入和输出
为了更好地理解输入和输出,我们将在本节中看一些计算思维问题。当我们设计算法的过程中,我们将专注于确定算法所需的输入以及我们从算法中需要的输出。让我们来看看我们的第一个问题。
问题 1 - 构建凯撒密码
凯撒密码是一种用于编码消息的密码系统。密码学用于保护信息,以便只有预期的用户可以阅读消息。凯撒密码使用字母的移位来编码消息。例如,字母 a 移动 3 个位置将变成 d。为了构建一个可以为我们做到这一点的算法,我们需要一些东西:
-
将要编码的消息
-
我们将每个字母移动多少
-
一个打印的编码消息
让我们思考一下前面提到的要点意味着什么。消息将需要用户输入。移位也将是一个输入,因为我们不希望程序总是使用相同的代码。否则,一旦知道了移位,原始消息就很容易被破解。打印的编码消息是我们的输出。以下是一些步骤,以帮助创建算法:
-
首先,我们需要输入消息。
-
然后我们需要定义输入移位。
-
之后,我们需要遍历每个字母。
-
迭代后,我们根据定义的移位调整每个字母。
-
最后,我们打印出新的编码消息。
我们将通过以下密码算法片段来举例说明前面的步骤:
ch9_problem1.py
#Print initial message for the user
print("This program will take your message and encode it.")
#Ask for the message
msg = input("What message would you like to code? ")
#Ask for shift
shift = int(input("How many places will you shift your message? "))
msgCipher = ""
#Iterate through the letters, adjusting for shift
for letter in msg:
k = ord(letter)
if 48 <= k <= 57:
newk = (k - 48 + shift)%10 + 48
elif 65 <= k <= 90:
newk = (k - 65 + shift)%26 + 65
elif 97 <= k <=122:
newk = (k - 97 + shift)%26 + 97
else:
newk = k
msgCipher += chr(newk)
print("Your coded message is below.")
print(msgCipher)
请注意,在迭代中,我们正在进行一些数学运算,以找出字母表中每个字母的值。我们使用一些条件语句来定义在使用用户定义的移位值后,每个字母的值是什么。
让我们看看当我们运行此算法时产生的输出:
This program will take your message and encode it.
What message would you like to code? Code this message
How many places will you shift your message? 2
Your coded message is below.
Eqfg vjku oguucig
正如您可以从前面的输出中看到的,注意消息和编码消息中的第一个单词 - Code:
-
C,向后移动两个字母,现在是E。 -
o现在是q,依此类推。
对于消息中的每个字母,字母都向后移动了两个位置。输出是我们的print()语句加上编码消息,由行print(msgCipher)给出。我们的输出包括两个语句以便清晰。第一个消息是必要的吗? 不。但是,在某些算法中,最好有一些行,让用户知道算法的运行情况。
让我们看看另一个问题。
问题 2 - 查找最大值
您被要求创建一个程序,该程序从数字列表中找到最大值。数字列表由用户提供。因此,为此问题创建一个算法。
首先,我们需要确定此特定问题的输入和输出。它们列如下:
-
列表中的项目数(输入)
-
列表中的数字(输入)
-
列表中的最大值(输出)
回想一下本章前面,在定义输入和输出部分,我们可以定义一个空列表,然后要求用户让程序知道将有多少项目输入列表。以下程序举例说明了相同的情况:
ch9_problem2.py
#Define the list name
maxList = []
#Ask user how many numbers will be entered
quant = int(input("How many data points are you entering? "))
#Iterate, append points, and find maximum
for i in range(0, quant):
dataPoint = int(input("Enter number: "))
maxList.append(dataPoint)
#Print maximum value
print("The maximum value is " + str(max(maxList)) + ".")
请注意,从前面的代码中,我们使用了max()函数来找到列表中的最大值。此外,我们还必须添加int()类型,以便算法正确识别值为数字。代码遍历从0到我们从用户输入中收到的数据点数,我们将其定义为quant变量。当算法遍历数字时,它会比较它们并找到最大值。
让我们看看输出是什么样子的:
How many data points are you entering? 6
Enter number: 1
Enter number: 2
Enter number: 8
Enter number: 4
Enter number: 5
Enter number: 7
The maximum value is 8.
正如您可以从前面的输出中看到的,用户声明将输入6个数字。然后算法提示用户输入每个值。最后,识别出最大值。
现在让我们看看如何构建一个猜数字游戏。
问题 3 - 构建一个猜数字游戏
您被要求构建一个游戏,用户可以根据需要获得尽可能多的机会来按正确顺序识别四位数的数字。当用户输入猜测时,算法将指出是否有任何数字是正确的。
算法将确定数字是否在正确的位置,但不会确定哪些数字是正确的,哪些在正确的位置。如果玩家猜对了数字,游戏就结束了。您可能会发现这个游戏类似于一个名为Mastermind的流行游戏,其中玩家将四个颜色代码插入游戏,第二个玩家猜测。
在这种情况下,我们使用数字而不是颜色,计算机程序将帮助我们确定是否有任何数字是正确的,以及它们是否在正确的位置。
为了构建这个游戏,让我们看看我们需要的输入和输出:
-
随机生成的 4 位数(输入)
-
用户的猜测(输入)
-
来自算法的数字和位置的反馈(输出)
-
在数字被猜中后,结束游戏的消息(输出)
当我们生成数字时,我们需要找到一种比较该数字的数字与用户输入的数字的方法。请注意,我们需要为这个特定的算法导入random库,以便我们可以生成随机的四位数。让我们看一下代码片段:
ch9_problem3.py
import random as rand
number = rand.randint(1000,10000)
guess = int(input("What's your first guess? "))
#Algorithm checks if number is correct.
if (guess == number):
print("That's right! You win!")
else:
i = 0
正如你所看到的,这是我们需要用户输入的一个例子。然而,这只是第一次猜测,所以我们需要从用户那里获取更多的输入,并提供一些提供反馈的输出。看一下来自同一算法的以下代码片段:
#Condition so that user keeps guessing until they win.
while (guess != number):
i = i + 1
#Remember you can also write as i += 1
j = 0
guess = str(guess)
number = str(number)
#Check which numbers are correct and mark incorrect with 'N'
guessY = ['N']*4
#Make sure you check each digit, so run loop 4 times
for q in range(0, 4):
if (guess[q] == number[q]):
j += 1
guessY[q] = guess[q]
else:
continue
#If only some digits are correct, run next condition
if (j < 4) and (j != 0):
print("You have " + str(j) + " digit(s) right.")
print("These numbers were correct.")
for m in guessY:
print(m, end = " ")
#Ask for next input
guess = int(input("What is your next guess? "))
注意,我们需要在每次猜测失败后询问每次猜测。算法需要知道下一次猜测是什么,以便在用户没有识别出正确的四位数时继续运行。这就是为什么猜测输入值在算法中出现在多个地方。
一旦用户猜对了,程序需要打印最终的声明,在这种情况下,我们会让用户知道猜测花了多少次:
#If only some digits are correct, run next condition
if (j < 4) and (j != 0):
print("You have " + str(j) + " digit(s) right.")
print("These numbers were correct.")
for m in guessY:
print(m, end = " ")
#Ask for next input
guess = int(input("What is your next guess? "))
#No digits correct
elif (j == 0):
print("None of these digits are correct.")
guess = int(input("What is your next guess? "))
if guess == number:
print("It took you " + str(i)+ " tries to win!")
正如你所看到的,这个算法在多个地方基于满足条件的输入,以及输出。输出包括print语句和对正确或错误数字的反馈。让我们看看程序执行时输出的样子:
What's your first guess? 1111
None of these digits are correct.
What is your next guess? 2222
None of these digits are correct.
What is your next guess? 3333
You have 1 digit(s) right.
These numbers were correct.
3 N N N What is your next guess? 3444
You have 3 digit(s) right.
These numbers were correct.
3 4 N 4 What is your next guess? 3454
You have 3 digit(s) right.
These numbers were correct.
3 4 N 4 What is your next guess? 3464
You won in 6 attempts.
请注意,在第三次尝试后,我们有一个正确的数字,表示为3 N N N。这意味着第一位数字是 3。然后我们需要猜测其余的数字。在程序中提供反馈,或输出,使用户能够继续这个游戏。
正如你所看到的,理解输入和输出使我们能够创建能够提供反馈、根据条件进行迭代等等的程序。
在这一部分,我们学习了如何在解决问题时使用输入和输出。我们提供了一些场景,让我们可以探索一些输入类型,比如用户定义的输入和算法定义的输入。我们还学习了如何编写算法,为用户提供清晰的输出。
总结
在本章中,我们讨论了 Python 算法中的输入和输出。输入是我们获取算法响应的信息的方式。例如,用户可以为需要提示信息的算法提供输入,就像我们在示例问题中看到的那样。Python 和其他编程语言也可以从鼠标、扫描仪、摄像头和程序交互的其他设备中获取输入。
为了编写我们的算法,重要的是要确定我们需要什么输入,我们在设计中何时需要它们,以及我们需要程序输出什么。在[第三章](B15413_03_Final_SK_ePub.xhtml#_idTextAnchor056),理解算法和算法思维中,我们使用用户输入来找到午餐的成本。我们创建了一个字典来帮助我们找到这些信息。同样,在阅读完本章之后,你现在有了构建算法来解决一些额外问题的技能,比如我们的数字猜测游戏和我们的最大查找器。
在设计算法之前,确定输入和输出是至关重要的,因为我们在算法中做出的条件和决策取决于输入和我们需要程序提供的输出。
在下一章中,我们将讨论控制流,这在理解算法的阅读方式和指令执行方式上至关重要。我们还将在探索控制流中更仔细地查看函数。

被折叠的 条评论
为什么被折叠?



