且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

《从问题到程序:用Python学编程和计算》——2.6 简单脚本程序

更新时间:2022-10-04 08:37:54

本节书摘来自华章计算机《从问题到程序:用Python学编程和计算》一书中的第2章,第2.6节,作者 裘宗燕,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

2.6 简单脚本程序

一个Python程序(脚本)是一个独立的文件,文件的扩展名用py,文件的内容应该是一些Python命令(语句)。把这种脚本送给Python解释器,令其执行,就能看到执行的效果。本节介绍脚本的建立和执行,以及程序在运行中与人交换信息的问题。[ 实际上,完全可以用任何文本编辑器,所有功能强大的Python程序开发环境也都提供了编辑Python程序的功能。可以根据自己的需要和考虑自行选择。但下面只考虑用IDLE编辑的问题。]

2.6.1 脚本的编辑和执行

一个Python脚本的内容是一系列Python命令,可以用任何文本编辑器建立这种脚本。官方Python系统的IDLE开发环境为建立和编辑Python脚本程序提供了很好的支持,用它的编辑器来编辑脚本是非常合适的。在IDLE执行环境(Shell)中打开一个新文件,IDLE就会打开一个编辑器窗口。我们可以在窗口中输入和编辑所需的程序。窗口的文件菜单里还有打开已有文件和打开最近文件(Recent Files)的命令选项,供使用者打开已有的文件,通过编辑给文件添加新内容,或者修改已有的内容。

IDLE的基本编辑功能与其他文本编辑器类似,特殊功能就是能自动维护Python程序的格式,用不同颜色突出显示程序里的各种成分,例如关键字、数值字面量、字符串等。用它编写Python程序非常方便。IDLE编辑器也支持各种常规的编辑操作,如通过键盘或鼠标移动编辑光标的位置,标记、复制、剪切和粘贴文本内容等。

脚本执行和输出

图2.1展示了IDLE编辑器窗口的一个现场情况,已经输入的脚本包含三个命令行,还有一个空行分隔导入命令和随后的赋值。这种空行不影响程序的意思,只是为了更清晰地展现其逻辑结构。根据前面的讨论,这些命令顺序执行后,变量area应记录着算出的三角形面积。

《从问题到程序:用Python学编程和计算》——2.6 简单脚本程序

新建的脚本需要保存为文件,相应文件操作与其他编辑器类似。Python程序文件应该以py作为扩展名。从标题行可以看到,图2.3中的程序已存入triangle.py。

编辑器窗口的菜单条与执行窗口不同,这里多了Format和Run两个菜单。Format菜单里的命令用于对程序格式做一些修改,Run菜单里的命令与程序执行有关。在要求解释器执行所编辑的脚本之前,必须先把编辑的内容存入一个文件。

点击Run菜单里的Run命令项,解释器就会执行当时正在编辑中的脚本程序。如果点击Run命令,要求运行图2.3里的程序,解释器就会转到IDLE的执行窗口,显示一些信息,然后显示提示符并停在那里。这时triangle.py脚本里的命令已经执行完毕。在交互执行窗口的提示符后输入变量名area,就可以看到该变量的值(计算结果)。

注意,每次通过Run命令执行Python脚本,解释器都将重新启动,清除原来已经建立并赋过值的所有变量,然后执行脚本的内容。在执行脚本的过程中,即使其中出现了表达式,解释器也不显示表达式求值的结果。如果希望在执行窗口中显示计算的中间结果或者最终结果,就需要在脚本里显式地调用内置函数print(打印输出函数)。

print函数的使用形式是:
print(表达式, 表达式, ……)

这里可以允许任意多个表达式实参。print执行时逐个求出这些实参的值并将它们输出,两项输出之间加一个空格作为分隔,产生了一次调用的所有输出后换一行。

如果修改图2.1中的脚本,将其改为:

from math import sqrt

s = (5 + 7 + 11) / 2
area = sqrt(s * (s - 5) * (s - 7) * (s - 11))
print("Area of the triangle:", area)

执行这个Python脚本,就会在执行窗口里看到下面显示:

>>> ===================== RESTART =======================
>>> 
Area of triangle: 12.968712349342937

第一行表示解释器重新启动,最后一行是执行脚本里print命令产生的效果。两个实参表达式的值顺序打印,用一个空格分隔。

一个Python脚本文件包含了一个Python程序,是该程序的实际体现。下面讨论中将不再区分这两个概念,说建立一个程序,就是指建立一个包含该程序的文件。

2.6.2 程序和输入

虽然上面给出的脚本能完成一些计算,并根据要求输出计算结果,但这种形式的脚本的功能太局限了。例如,计算三边长分别为5、7、11的脚本只能计算这个特定三角形的面积。换一个三角形,虽然计算方法一样,该脚本也不能直接用,需要另写一个,或者在已有脚本的基础上修改。我们更希望有能从任意“给定三边长计算三角形的面积”的程序。“给定三边长计算三角形的面积”是一个(计算)问题,而“给定三边长5、7、11计算三角形的面积”是这个问题的一个具体例子,称为该问题的实例。

假设现在需要做一个能从“给定三边长计算三角形的面积”的程序,其每次执行计算一个具体三角形的面积。显然,在写这个程序时,我们不知道将来用程序的人(用户)需要计算哪个三角形的面积。进一步说,同一程序的不同执行可能计算不同的三角形。这实际上要求该程序能完成对任意三角形的计算。基于三边计算三角形面积的方法原本就是通用的,可用于计算任何一个三角形,而不同三角形的特点就在于边长不同。为了完成工作,这种程序必须有办法从用户那里得到具体实例的相关信息。具体说,计算三角形面积的程序必须为用户提供一个通道,让用户能为其提供具体三角形的三条边长。

输入函数input

程序在执行中从外部获得信息的操作称为输入。Python程序通过内置函数input从使用者那里获得信息。调用函数input(字符串),其返回值就是该函数执行时的用户输入。这个调用表达式的语义(效果)比较特殊,包括下面几步:

1)程序执行input(字符串)时,实参字符串将被输出到当时的交互执行环境,因此,这个字符串也就是一个要求使用者输入的提示,相当于Python解释器的提示符,通常称为提示符字符串或提示串,可以根据需要任意选择;

2)输出提示符后程序等待,直到用户输入了一些字符并按Enter键输入换行符;

3)input将用户在Enter键之前的输入做成一个字符串,作为函数的返回值。
由于input的返回值是字符串,程序计算需要的数据是浮点数,前面讨论的类型转换(从字符串转换得到浮点数)就有用了。

现在利用函数input,修改前面的triangle.py,加入输入命令,改造成一个通用的已知三边长求三角形面积的程序:

from math import sqrt

a = float(input("Length of edge a: "))
b = float(input("Length of edge b: "))
c = float(input("Length of edge c: "))
s = (a + b + c) / 2
area = sqrt(s * (s - a) * (s - b) * (s - c))
print("Area of the triangle:", area)

这里增加了三个输入语句,语句中包括了input调用,以及从字符串到浮点数的转换和赋值。执行前三个语句时,程序将逐一从用户那里获得三条边的长度。提示符字符串最后的空格只是为了清晰。这个程序一次执行时的情况如下:

>>> 
Length of edge a: 5
Length of edge b: 7
Length of edge c: 11
Area of the triangle: 12.968712349342937

其中的斜体表示程序输出,随后的正体字是我们(用户)的实际输入。如果重新执行,输入另一三角形的边长,就能得到那个三角形的面积。

这一段脚本与前面写过的东西都不同,它真正是解决一个问题的程序,可以处理该问题的任意实例,给出该实例的解。总结一下,这个工作分为几部分:

1)确定了三角形实例的一种描述方式:用三条边长描述三角形;

2)找到了一个通用的从三边长计算三角形面积的方法(直接来源于数学),基于这种方法写出了一段实现相应计算的程序(脚本);

3)代码中包含了获取具体三角形信息(输入)和输出计算结果的部分。

易见,即使同样计算三角形面积,也可以提出不同的计算问题。例如,用两个相邻边的长度和两边夹角作为问题实例的描述方式,就需要另一种计算方法。后面还会看到,即使是同一个计算问题,也可能有不同的计算方法,写出不同的程序。

上面总结具有普遍意义,在解决任何具体问题的过程中,都需要完成这几部分工作。在下面的讨论中,读者还会反复看到这些情况。

输入的问题

细心的读者可能已经想到,前面计算三角形面积的程序有问题:实际上,并不是任意三个实数都描述了一个三角形。要成为一个三角形的三边长,这三个数必须满足一定条件:边长不能是负数,而且要求任意两条边的长度之和大于另一条边的长度。上面的程序简单接受三个输入,就把它们当作三边长去算面积,如果输入不合适,就会出现以下情况:[ 如果没发现这个问题,应该认为是主动思考不足,需要在进一步学习中注意。]

>>> 
Length of edge a: -2
Length of edge b: 6
Length of edge c: 10
Traceback (most recent call last):
  File "D:/MyBooks/ptop-Python/Progs/triangle.py", 
      line 7, in <module>
    area = sqrt(s * (s - a) * (s - b) * (s - c))
ValueError: math domain error

可以看出,计算中出错了,解释器报告在执行数学函数时出现定义域错误(domain error),这里具体的情况是数学函数sqrt遇到负的实参,无法完成计算。

一个程序在执行中出错,不能给出结果,以非正常的方式终止,这种情况经常被说成是这个程序在执行中崩溃,或简单地说该程序崩溃了。从写程序的角度说,我们总希望程序能正确处理尽可能多的情况,而且不轻易崩溃。这个问题很值得考虑。

用户输入来自程序之外,不受程序的控制。写程序的人无法规定用户必须提供哪些输入。在这种情况下只能换个角度考虑问题:设法在程序里做一些检查,在确定了用户提供的输入满足程序要求之后再做计算;如果不满足要求,就设法提供一些信息,帮助用户了解为什么送给程序的输入是错误的。当然,后一类情况的处理要看实际需要,上面说的输出错误信息只是一种处理方式,后面还会讨论其他处理方式。

这里我们遇到了一个新问题:需要检查程序的输入,而且需要根据检查情况确定不同的处理方式。前面的程序都是直线型的程序,做完一个操作后再做下一个,直至程序结束。现在需要根据情况分别处理,计算要出现分支。为此需要两方面的新机制:

1)检查数据的情况,判断一些条件是否成立;

2)根据条件的成立与否,选择不同的操作或者操作序列。

下一节介绍解决这个问题的Python结构和编程技术。

注释

程序可能很长,人们经常需要在里面写一些说明性的文字,帮助读程序的人们理解程序的行为,了解写程序人的一些想法,还可能希望说明一下程序里的特殊处理方式等。实际上,需要读程序的人首先是我们自己。我们写了一些程序,过几天又可能回来读它们。程序里的说明性文字只是为读程序的人服务,并不希望解释器为它们做任何事情。这种写在程序里的说明性文字称为注释,其作用就是给人阅读。

Python程序里的注释用一个井号字符(#)开头,一直延伸到该字符所在行末尾。解释器处理中遇到 # 字符,就把它和直到换行的一段字符丢掉,不做任何处理。人们提倡把较长的注释写成独立的行,写在普通语句之后的短小注释也很常见。

在下面的程序实例中,我们有时会写一些注释。