经过第一讲的学习很多读者已经能用Python构造简单的程序了,估计很多人的疑惑又来了,我们用前面简单的指令好像不能解决我们实际问题诶,总是感觉差点什么,数字和字符串可以处理了,但是我们日常任务分情况处理的情形好像处理不了,还有很多重复性的工作,我们利用我们学过的指令处理的话,也要一条条的重复指令才能处理,这个重复写指令的时间好像不比我们人工处理的时间短,还有种情况,利用前面学过的指令,对于很多暂时不能确定的行为我们不知道如何处理,Python的“控制结构语句”就可以帮助我们解决上面的疑惑,通过合的使用控制结构,我们可以处理上面提及的所有情形,这一讲会详细的阐述控制结构的相关内容,学习完本讲,你应该就可以构造出更完美的程序。
控制结构语句和流程图中的符号有天然的一致性,使用图形表示算法的思路是一种极好的方法,因为千言万语不如一张图。本章开始我们稍微了解一下流程图,掌握其简单的使用方法。之所以在本章中使用流程图辅助,原因是其可以帮助我们快速理清用自然语言描述的做事步骤,帮我们全面理解做事流程和快速构造程序,所以在本章中,在构造程序之前,对于稍微复杂点的功能,我们会先绘制流程图,然后再编写代码。图2-1所示是修理电灯的流程图,内容是修理电灯的步骤,通过箭头的指向表明修理过程中的事件组成,通过流程图可以很直观的了解从开始到结束的所有步骤。
图2-1 电灯不亮处理流程图,
在流程图中,从开始到结束通常有不止一条路径,计算机代码本质就是日常生活流程的拷贝,所以计算机程序运行过程中也应该和我们日常生活处理流程一致。流程图中的菱形表示是分支节点,在这个节点上会根据条件不同做出不同的选择,开始步骤用椭圆表示、结束步骤用带半圆的矩形表示,其他步骤也就是业务处理部分用带圆角的矩形表示。
但在学习控制结构之前,我们首先要学习如何表示这些“是”和“否”选项,同时你也需要理解如何将分支节点转换成Python代码。在正式学习控制结构之前,我们首先要学习布尔值、比较运算符和布尔运算符等基础知识。
2.1 Boolean类型
Boolean类型是特殊的类型,和我们在第一讲中讲的整型、浮点型和字符串数据类型不一样,那三个类型的取值都有无数种可能,而Boolean类型只有两种取值可能:True和False。(Boolean的首字母大写,这个数据类型是数学家乔冶· 布尔的姓氏,主要就是为了纪念这位数学家。),在Python代码中,布尔值True和False不是字符串,两边没有引号,它们总是以大写字母T和F开头,后面的字母小写,为了有个直观的认识,最好是在开发环境或者交互式环境中直观进行测试:
>>> testVariable=False
>>> testVariable
False
>>> False
False
>>> false
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'false' is not defined
>>> True
True
>>> true
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'true' is not defined
>>> True=3
File "<stdin>", line 1
SyntaxError: cannot assign to True
>>> False=5
File "<stdin>", line 1
SyntaxError: cannot assign to False
通过前面的联系我们看到,像其他类型值一样,布尔值既可以保存在变量中(testVariable),也可以用于表达式中。如果大小写不正确(false,true),或者使用True和False作为变量名,Python就会给出错误信息。
2.2 关系运算符
关系运算符也称比较运算符,在Python中其作用是比较符号两边的两个值,判定两边的值是不是符合运算符的语义,如果符合就是True,如果不符合就是False。表2-1列出了关系运算符。
表2-1关系运算符
运算符 |
含义 |
== |
等于 |
!= |
不等于 |
< |
小于 |
> |
大于 |
<= |
小于等于 |
>= |
大于等于 |
下面我在交互环境中这个演示这些符号的使用,如果两边的值一样,==(等于)求值为True。如果两边的值不同,!=(不等于)求值为True。==和!=运算符实际上可以用于所有数据类型的值:
>>> "python"=="python"
True
>>> "计算机与信息工程学院"=="计算机与信息工程学院”
File "<stdin>", line 1
"计算机与信息工程学院"=="计算机与信息工程学院”
^
SyntaxError: EOLwhilescanning string literal
>>> "计算机与信息工程学院"=="计算机与信息工程学院“
File "<stdin>", line 1
"计算机与信息工程学院"=="计算机与信息工程学院“
^
SyntaxError: EOLwhilescanning string literal
>>> "计算机与信息工程学院"=="计算机与信息工程学院"
True
>>> "计算机与信息工程学院"=="计算机"
False
>>> 45==45
True
>>> 45==100
False
>>> "计算机"!="计算机"
False
>>> "计算机"!="计算机与信息工程学院"
True
>>> 20.2!=20.2
False
>>> 20.2!=20.1
True
>>> True==False
False
>>> '100'==100
False
>>> True!=False
True
>>> 1==True
True
>>> 0==False
True
>>> 2==True
False
>>> True=='1'
False
>>> False=='0'
False
请注意,整型或浮点型的值永远不会与字符串相等,注意两个特殊的数值,0和1,True和False。
<、>、<=和>=运算符大多数场合用于整型和浮点型值,但这几个运算符还可以用于字符串的比较:
>>> 1<2
True
>>> 1>2
False
>>> 1<1
False
>>> 1<=1
True
>>> 1>=2
False
>>> 1>=1.0
True
>>> var1=3
>>> var1<3
False
>>> var1<=3
True
>>> var2=34
>>> var1<var2
True
>>> var2>=20
True
>>> 'a'<'b'
True
>>> 'abcd'<'abce'
True
>>> 'ab'>'aA'
True
>>> 'A'>'a'
False
注意字符串的比较是以ASCII码为顺序的比较,’a’的ASCII码是97,大写的’A’是65,所以比较的时候才出现‘A’>’a’的结果为False的情况,这个与我们直觉上完全不一致,比较运算符两边的类型必须完全相同
>>> 1>'a'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '>' not supported between instances of 'int' and 'str'
但有一点例外,Boolean类型和数值型可以出现在比较运算符的两边:
>>> 1.0==True
True
>>> 1==True
True
>>> 1>True
False
字符串的比较是是对位比较,也就是对应位置的字符相互比较,以对应位置字符的ASCII码的大小为基准,以第一个有比较结果的值为结果,比如:
>>> 'ABee'<='Ace' #第一位是A相同,第二位左边:B 右边:c,显然大写小于小写
True
>>> 'abce'<'aBc' # 第一位相同,第二位左边:b 右边:B,显然小写大于
False
>>> 'abce'<'aBce'
False
>>> 'abce'<'aB'
False
>>> 'abce'<='aB'
False
在学习过程中我们要注意=运算符,和我们数学里面学到的=号的不同,在我们数学概念中,=号完成两个语义,比较和赋值,使用过程中通过相关的文字描述予以区分,但在计算机的世界中,无法使用相关的辅助信息来区分,这个时候我们把=运算符的两个语义做明确的区分,=运算符仅表示赋值,而比较的语义呢,我们创造另一个运算符完成,这就是我们后面会经常使用的==运算符,准确的描述如下:
==(等于)运算符用于确定两个值是否彼此相同。
=(赋值)运算符将右边的值放到左边的变量中。
当然还有数学里面的不等于!=,这个在Python用了等同运算符表示,含义完全相同,其他的计算机语言可能会用<>符号表示,但Python中使用与数学符号完全相同的符号!=。
2.3 布尔运算符
我们日常生活中做事时候,通常会碰到需要一系列条件都具备才继续的事件,描述的时候我们会用自然语言相关词汇予以准确表示,比如买物品,只有我们钱包里面的钱足够而且对应产品有货时,我们才可以继续我们的购买行为,我们通过“只有 ......而且......”这类句型来表示两个条件都具备才会做继续购物,这就是条件的情形我们称之为与。当然在实际的生活中还会有多个条件具备其一我就可以继续做事过程的情形,比如如果明天放假或者我手边有车的话,我就去栖岩寺溜达一圈,通过”或者”这个词来表明,两者中任何一个条件具备我就去栖岩寺,当然两者条件都具备更好,这种情形我们称之为或。生活中还我们还会有如果某个条件不成立的话,我才能做某事,比如明天不下雨我我就去爬山,通过’不’词来表示否定,这种情形我们称之为非。
笔者在一开篇就说了,计算机的程序是对人类生活场景的模拟和拷贝,那么在计算机中显然我们也必须能用相关的关键字来表示我们前面描述的条件的几种情形,怎么描述呢?我们自然语言的千变万化,表示同等条件的词语会很多,我们人可以通过我们的大脑,很轻松的理解文字中的意思和条件组合的情况,但计算机不能象人类这样灵活,于是我们规定三个固定的三个符号或者词语来表述’与’、’或’、’非’语义,最终在语言中使用什么样的符号表示这个条件语义,每个语言不一样,Python中,我们用and、or和not来表示条件如何连接,然后为了容易理解和记忆,我们把这个三个条件连接符称为布尔运算符,当然这个只是从容易理解角度来表述的,严格来说,为什么在计算机用这三个条件表示符就能完备表示我们日常生活的所有逻辑是有严格数学理论支撑,有兴趣的读者可以在数理逻辑的相关章节中找到严谨的证明。
接下来我们在明确一个概念条件表达式,简单来说凡是可以求值为一个具体布尔值的表达式我们都可以称之为条件表达式,比如:1>2,’1’!=’@’,布尔运算符的作用就是用于组合条件表达式形成更复杂条件表达式的运算符,通过布尔运算符组合成的复杂条件表达式最终也能求值为一个布尔值。让我们仔细看看这些运算符,从and运算符开始。
2.3.1 二元布尔运算符
所谓的二元布尔运算符就是至少需要两个布尔值或者表达式参与运算才能形成具体的布尔值的符号,and和or运算符就是,这两个运算符须在其两边都有具体的布尔值或者可以计算为具体布尔值的表达式才能完成具体的计算,所以它们被分类为“二元”运算符,其计算规则如下:如果两个布尔值都为True,and运算符就将表达式求值为True;否则求值为False,对于or运算符,只有参与计算的两个布尔值都为False,or才将表达式求值为False,否则求值为True:
>>> True and True
True
>>> True and False
False
>>> False and True
False
>>> False and False
False
>>> True or True
True
>>> True or False
True
>>> False or False
False
>>> False or True
为了能更清晰的看到计算结果,笔者这连个运算符的计算规则用两个表格显示出来,表2-2所示为and运算符的计算结果,表2-3or运算符的运算结果:
表2-2 and运算符的计算结果
表达式 |
求值为 |
True and True |
True |
True and False |
False |
False and True |
False |
False and False |
False |
表2-3 or运算符的计算结果
表达式 |
求值为 |
True or True |
True |
True or False |
True |
False or True |
True |
False or False |
False |
看到前面的计算过程,学过布尔带书或者谓词逻辑的同学,一下子就明白了,这不就是真值表嘛,简单,当然没学过类似前置知识的同学,记住这个概念就行了,“真值表”显示了布尔运算符的所有可能结果。表2-2所示为and运算符的真值表,表2-3所示为or运算符的真值表
2.3.2 not运算符
与and和or不同,not运算符只作用于一个布尔值或表达式,这种类型的运算符我们称之为:“一元”运算符。not运算符的计算过程就是就是对其作用的布尔值或者表达式取反:
>>> not True
False
>>> not False
True
>>> not not not not True
True
>>> not not False
False
和我们使用自然语言说话和写作一样,我们在计算机的表达式中也可以使用多重否定,比如可以在not之前在使用not,然后还可以使用not。表2-4为not运算符的运算结果,当然也可以称之为not运算的真值表。
表2-4 not运算符的运算结果
表达式 |
求值为 |
not True |
False |
not False |
True |
2.4 布尔和比较运算符的混合使用
使用比较运算符构成的表达式可以求值为布尔值,使用布尔运算符构成的表达式也能求值为布尔值,显然我们在写计算机程序的时候,可以混合使用布尔运算符和比较运算符构成复杂的表达式来准确表述自然语言中的复杂条件,当然这个组合使用也有一定的规则限制。
回忆一下我们前面刚讲过的比较运算符,比较运算符两边只需出现相同的数据类型,就可以比较,最终的比较结果为布尔值,而上面刚学习的布尔运算符and、or和not只能操作布尔值,这就要求我们在写程序的过程中就要严格注意其语法约束,不能在布尔运算的两边或者一边出现非布尔值,而比较运算符两边只要类型相同即可。多个可以求解成布尔值的表达式可以通过布尔运算符连接成一个更大更复杂的表达式,然后这个复杂的表达式可以作为另一个布尔表达式的一部分,例如:1<2是比较表达式,可以求值为一个布尔量,这个表达式可以出现在 1<2 and 4>5布尔表达式中,同样这个1<2 and 4>5,亦可以一个整体出现在一个更大的布尔表达式中,还有就是为了看起来更直观,我们经常用括号把一个有明确值或确定值的比较表达式或者布尔表达式给括起来,以方便代码阅读或者防止出现符号优先级不同引起的计算顺序和我们设计的不一样,同一个表达式,括号位置不一样,最终会出现不一样的计算和比较结果,这个过去我们学数学的已经得到验证,这个规则在计算机的世界也适用,看下面的例子:
>>> 1<2
True
>>> 4>5
False
>>> 1<2 and 4<5
True
>>> 1>2 and 4<5
False
>>> 1>2 or 4<5
True
>>> 1==3 or 4<5
True
>>> (1<2) and 5<6
True
>>> (1<2) and (5<6)
True
>>> 1>2 and 4<5 or 3<4
True
>>> 1>2 and (4<5> or 3<4) #这个演示个异常
File "<stdin>", line 1
1>2 and (4<5> or 3<4)
^
SyntaxError: invalid syntax
>>> 1>2 and (4<5 or 3<4)
False
>>> (1>2 and 4<5) or 3<4
True
在复杂表达式中,计算顺序是从左边开始向右计算,计算机将先求值左边的表达式,然后求值右边的表达式,如果有括号,先计算括号内的表达式。得到两个布尔值后,在将整个表达式再求值为一个布尔值;比如:1>2 and 4<5 or 3<4 表达式,先计算1>2 为false,然后计算4<5 为True,然后计算3<4为True,表达式演变成:False and True or True,计算还是从左向右 False and True 为False,False or True 为True,所以在没有括号的情况下,最终的计算结果是:True。
带有括号的表达式1>2 and (4<5 or 3<4)的计算过程是这样的:1>2为False 然后向右计算碰到括号,先计算括号的值 4<5 为True,3<4为True,括号内的计算为(True or True) 其结果为True,最后整个表达式计算为:False and True,结果为False。
当然在表达式中还可以使用数学计算过程和Python的函数或者我们自定义的函数,表达式的计算顺序还有从左向右,先计算优先级高的运算,也可以在一个表达式中使用多个布尔运算符和比较运算符,例如:
>>> 3+4==7 and not 2+1==6 and 2*2==6 and 2*5==4*4 or 'abcsd'>'Ab'
True
通过前面的例子不难看出,和数学算术运算符一样,布尔运算符也有操作顺序,在Python中计算顺序在没有括号的情况下,从左向右,在所有算术和比较运算符求值后,Python先求值not运算符,然后求值and运算符,最后求值or运算符。
2.5 控制结构的基本形式
控制结构通常以“条件”开始;接下来是一个代码块,称为“语句块”。在开始学习具体的Python控制结构之前,我先介绍条件和语句快。
2.5.1 条件
前面学到的布尔表达式可以看成条件,它和表达式是一回事。“条件”只是在控制结构的上下文语境中更具体的名称,其最终会求值为一个布尔值:True或False。控制结构语句根据条件是True还是False,来决定做什么,几乎所有的控制结构都需要配合条件使用。
2.5.2 语句块
一些执行有先后顺序的代码行通常会可以看成一组,放在一个紧凑的“语句块”中,在其他语言中通常可以使用相关的符号予以标识,但在Python中,没有使用相关的符号,而是根据代码行的缩进判断语句块的开始和结束,语句块构成有以下3条规则。
- 缩进增加时,新的语句块开始。
- 语句块可以包含其他语句块。
- 缩进减少为零,或与外面包围语句块对齐,语句块就结束了。
这些规则估计初学者看着很晕乎,下面我们看例子:
userName='testuser'
userPassword='testPassword'
if userName=='testuser' and userPassword=='testPassword':
print("你好"+userName+'!')
print("进入系统的主界面或者Web应用的主页!")
else:
print("密码或者用户名错误!")
if userName!='testuser':
print('用户名错误')
if userPassword!='testPassword':
print('密码错误!')
可以在PyCharm上新建一个程序,将代码拷贝到编辑器中执行,第一次执行不做任何修改,能看到如下的反馈信息:
你好testuser!
进入系统的主界面或者Web应用的主页!
然后修改userName=’testuser1’和userPassword='testPassword1',在执行这个代码能得到如下反馈信息:
密码或者用户名错误!
用户名错误
密码错误!
通过userName和userPassword不同初始值执行代码,自然而然就会发现,两次运行的走的语句一样,而且每次只要进入其中一个代码分支,另一个分支就永远得不到运行,而且,运行完print("你好"+userName+'!')的语句后,print("进入系统的主界面或者Web应用的主页!")在没有外部人力干预的情况下会连贯的执行下去,同样,执行执行了print("密码或者用户名错误!"),其后的两个If语句都不可避免的被执行,直觉上第一个语句块开始于:
print("你好"+userName+'!')
这个显然是符合规则1
紧接其后的print("进入系统的主界面或者Web应用的主页!"),和前一个print的没有代码的缩进增加,也没有代码缩进减少,那么这两句属于同一个语句块。
紧接else:语句,缩进减少了,根据规则3,这个语句块结束了
接着else:语句之后,又有新的缩进,根据规则1,新的语句块开始了:
print("密码或者用户名错误!")
if userName!='testuser':语句的没有代码的缩进增加,也没有代码缩进减少,显然和前面的一句print属于同一个语句块,
接着出现新的情况,在if userName!='testuser':之后出现了新的缩进,按照规则1,新的语句块又开始了,执行完print('用户名错误')语句后,代码缩进又减少了显然这个语句块结束了,接着下一句if userPassword!='testPassword':缩进和print("密码或者用户名错误!")一致,显然if userPassword!='testPassword':和print("密码或者用户名错误!")属于同一语句块,接着print('密码错误!')又出现新的缩进,新的语句块开始,执行完后,缩进减少,说明print('密码错误!')语句块结束,接着又缩进减少,说明 print("密码或者用户名错误!")和之后的两个if构成的语句块也结束了,最后缩进到0,最后没有代码了,说明整个程序结束了。
在代码结构中我们可以清晰看到,语句块可以包含其他的语句块,语句块的隶属通过语句前面的缩进为标准,缩进的作用和C语言的{}的作用相同,在书写代码是的时候,不建议使用空格来实现缩进,因为在敲代码的时候,使用空格的个数量每个人习惯不同,请用TAB实现标准缩进。
2.6 程序执行
在上面的程序中,Python从程序顶部开始,然后一条接一条往下执行指令。“程序执行”(或简称“执行”)这一术语是指执行当前的代码行中确定的操作或者计算。如果将源代码打印在纸上,在它执行时用手指指着每一行代码,你可以认为手指就是在做程序执行。
但是,并非所有的程序都是从上至下顺序简单地执行,如果我们跟踪一个带有控制结构的程序,可能会发现在程序的运行中会根据控制结构中条件最后计算的值不同,有时候跳过一些语句块,有时候又会重复执行某一个语句块,且有时候会什么都不做,直接结束程序。
2.7 控制结构
现在,让我们来看我写程序过程中会时刻碰到的两种结构:分支结构和循环结构。分支结构就是用来翻译图2-1流程图中的菱形的语句,在Python中使用if语句表示,它们是程序将做出的实际判断,根据条件重复执行某一语句快的结构称之为循环结构,在Python常用While语句和For语句来表示,接下来逐个讲解每个结构组成和细节。
2.7.1 分支结构
1、if语句
最常见的分支结构语句是if语句:if语句根据其判定条件的真假,决定其后语句块的执行路径,如果条件为True,则执行紧跟if语句的语句块,如果条件为False,将跳过之后语句块。
在自然语言中,if语句表述的意义是:“如果条件为真,执行其后语句块中的代码。”在Python中,if语句包含以下部分。
- if关键字。
- 条件(即求值为True或False的表达式)。
- 冒号。
- 在下一行开始,缩进的代码块(称为if子句)。
例如,以下代码,用于检查userName是否为testuser和userPassword是否为testPassword,如果userName是testuser且userPassword是testPassword,执行其后语句块:
if userName=='testuser' and userPassword=='testPassword':
print("你好"+userName+'!')
print("进入系统的主界面或者Web应用的主页!")
所有的控制结构语句都以冒号作为结尾,后面跟一个新的语句块。语句if子句是一个语句块:包含print("你好"+userName+'!')和print("进入系统的主界面或者Web应用的主页!")。
2、else语句
if子句后面有时候会跟一个else语句,当if的条件表达式计算为False时,else子句就会被执行。在自然语言中,else语句表示的意思是:“如果条件为真,执行if后跟着的语句块;否则,执行else后跟着的语句块”,else语句不包含条件表达式,else语句形式如下:
- else关键字。
- 冒号。
- 在下一行开始,缩进的语句块(称为else子句)。
回到前面的例子,我们看看使用else语句的一些代码,当userName是testuser或者userPassword是testPassword,有任意一个条件不成立,最后表达式(userName=='testuser' and userPassword=='testPassword')就为假,这样就进入了else之后的跟着的语句块:
if userName=='testuser' and userPassword=='testPassword':
print("你好"+userName+'!')
print("进入系统的主界面或者Web应用的主页!")
else:
print("密码或者用户名错误!")
if userName!='testuser':
print('用户名错误')
if userPassword!='testPassword':
print('密码错误!')
进入else语句块后,首先执行print("密码或者用户名错误!"),然后在执行if userName!='testuser'语句,根据userName的值决定是否进入其下的语句块,然后在执行if userPassword!='testPassword',根据userPassword的值决定是否执行下级语句块,然后结束else之后的语句块,然后结束程序的执行。
3、 elif语句
在我们实际生活会碰到着这样的生活场景:小时候每年过年都是我们最开心的时候,为什么,可收到长辈们给压岁钱,收到的都是红通通的毛主席头票,我们每年收到压岁钱都会想,要是今年我收到一张呢,我就可以买我心仪很久的大黄蜂的玩偶了,要是能收到两张,嘿嘿,我就可以心仪很久的那套灌篮高手的漫画套装了,要是能收到三张,变形金刚的套装我就能抱着睡觉了,要是能收到四张的话,今年就发达了,嘿嘿,我就可以买个的MP3了,让我们的同伴的羡慕嫉妒恨去,如果能收到四张以上,那么就可以...美梦做做就算了,不要想好事了,还有中最伤心的情况,一张毛爷爷头像头没收到,什么计划都只能想想了,这个时候我们尝试着用刚学过的条件语句把这个逻辑写下来,我们刚学过if-else结构,我们国家红通通的毛主席头票的面额是100,我们使用前面学过的流程图把我收到压岁钱后可能的购买行为给图形化一下:
图2-1 收到压岁钱后可能的购买行为流程图
我们利用学过的if—else结构把流程图翻译成Python代码会是以下形式:
giftmoney=500
if giftmoney==100:
print("大黄蜂的玩偶!")
else:
if giftmoney==200:
print("灌篮高手的漫画套装")
else:
if giftmoney==300:
print("变形金刚的套装")
else:
if giftmoney==400:
print("购买MP3")
else:
if giftmoney>400:
print("买更好的心仪的东西!")
else:
print("没收到压岁钱!")
当然有人可能会直接利用if结构写成如下形式:
giftmoney=0
if giftmoney==100:
print("买大黄蜂的玩偶!")
if giftmoney==200:
print("买灌篮高手的漫画套装")
if giftmoney==300:
print("买变形金刚的套装")
if giftmoney==400:
print("买MP3")
if giftmoney>400:
print("买更好的心仪的东西!")
else:
print("没收到压岁钱!")
咋一看,两段代码都没问题,但是仔细分析你会发现,第一种写法正确翻译了流程图,但if-else结构会越套越深,如果条件过多的,那么嵌套的你会无法忍受。第二种写法仔细看,显然没有正确翻译流程图的结构,还有无论收到多少钱都会每个判定条件比较一下,和我们正常的购买行为不一致,我们收到100块钱压岁钱,买完了大黄蜂的玩偶,手里已经没有钱了,显然不会在做后面的尝试,当然从计算机的角度这个没问题,但是增加了比较的次数,可能你会说不久这几次吗?小意思,但是试想一下,如果这样的的代码要反复运行很多次呢?那么占用CPU的时间可观了。为了更好组织代码和减少比较的次数,Python提供了elif语句,其意义是“否则如果”,跟在if或另一条elif语句后面,做另一个条件表达式计算,仅在前面的条件为False时才检查该条件。在代码中,elif语句的结构如下:
- elif关键字。
- 条件(即求值为True或False的表达式)。
- 冒号。
- 在下一行开始,缩进的代码块(称为elif子句)。
有了这个结构之后,下意识的可以改写流程图2-1的实现了,但是else怎么实现呢?简单,在最后的elif语句后面加上else语句即可。使用这种结构,在特定条件满足下,有且只有一个子句会被执行,如果每个if和elif语句中的条件都为False,就会执行else子句
我们上面的逻辑实现就可以以下形式出现:
giftmoney=0
if giftmoney==100:
print("买大黄蜂的玩偶!")
elif giftmoney==200:
print("买灌篮高手的漫画套装")
elif giftmoney==300:
print("买变形金刚的套装")
elif giftmoney==400:
print("买MP3")
elif giftmoney>400:
print("买更好的心仪的东西!")
else:
print("没收到压岁钱!")
仔细分析上面代码,现在和图2-1的流程图一致,而且结构上更清晰,在我们写代码的时候,这类控制流结构意义是:“如果第一个条件为真,做第一个IF后面的语句块;如果第二个条件为真,执行其后语句块;等等 如果所有条件都不满足,执行else后面的语句块”。在同时使用if、elif和else语句的时候,总是只有一个if语句,所有需要的elif语句都应该跟在if语句之后;其次,如果希望确保至少一条子句被执行,那么在最后加上else语句。
2.7.2 循环结构
对大多数人来说,周而复始地做同样的事情是非常枯燥的。那为什么不让计算机来替我们做这样的事情呢?计算机永远都不会觉得枯燥,它们非常擅长执行重复的任务。在小节中,我们就来看看如何让计算机做重复的事情。
计算机程序通常会周而复始地重复着同样的操作步骤,这称为循环(loop),主要有以下两种类型。
重复一定次数的循环,这叫作计数循环。
重复直至某种情况发生时才结束的循环,这叫作条件循环。只要条件为真,这种循环就会一直持续下去。
1、计数循环—for循环
通常计数循环我们也称之为for循环,这是因为包括Python在内的大多数编程语言都使用关键字for来创建这种循环。
在代码中,for循环语句的形式为,总是包含以下部分:。
- for关键字。
- 一个变量名。
- in关键字。
- 一个列表或者调用range()函数,这个函数最多传入3个参数。
下面我们来编写一个使用计数循环的程序。打开PyCharm,在工程的上右键选择New->Python File,弹出一个输入文件名的窗口,输入ch2_7_4_1for.py,然后键入如下代码:
for item in [1, 2, 3, 4, 5,10]:
print("你好!"+str(item))
在代码文件上右键选择Run ‘ch2_7_4_1for’,或者使用快捷键CTRL+SHIFT+F10运行这个程序,你会看到这样的结果:
你好!1
你好!2
你好!3
你好!4
你好!5
你好!10
这个时候你屏幕运行结果窗口是不是有重复?在代码中虽然只有一条 print 语句,但程序显示了6次“你好!”,同时每次跟上从[1, 2, 3, 4, 5,10]取出的一个数。这是怎么回事呢?
第一行(for item in [1, 2, 3, 4, 5, 10]:)翻译过来有下面3层含义。
- 变量item的值从 [1, 2, 3, 4, 5, 10]中取值,默认从第1个值开始,而[1, 2, 3, 4, 5, 10]的第一个值是:1,所以item = 1。
- 循环会依次从[1, 2, 3, 4, 5, 10]列表中的取出每一个值赋予Item,使用这个值的item把for紧跟语句块中的所有操作执行一次。(列表就是中括号中的那些数字。)
- 在每次执行循环时,变量Item都会被赋予列表中的下一个值。
第二行(print("你好!"+str(item)))就是 Python 每次循环时都要执行的代码块。for 循环需要一个代码块来告诉程序每次循环时具体做什么。这个代码块(代码中缩进的部分)称为循环体。还记得吧?第1章讨论过代码缩进和代码块。
这里在引入一个概念:每次执行循环称为一次迭代。
下面来试试一个使用for循环做求和计算的例子,当每次循环时,程序不再打印内容,而是把取出的整数累加,最后打印列表中所有数字的总和,同样新建ch2_7_4_1forsum.py,输入如下代码:
sum=0
for item in [1, 2, 3, 4, 5,10,17]:
sum=sum+item
print(sum)
把这个程序保存并运行。结果如下所示:
1
3
6
10
15
25
42
这一次不再打印7次的内容了,而是打印出列表中前几个数字的求和,每次执行循环题后,Item都会从列表中取出下一个值参与下一次求和。
使用循环解决问题的时候,经常会因为写错一个变量或条件,造成程序无法停止下来,这种循环我们称之为无限循环或死循环。在Python中,我们想随时停止 Python 程序直接方法是:按下CTRL+C快捷键,即在按下CTRL键的同时按下 C 键,当你按下这个组合键后,程序就会停止运行,死循环和问题循环也不例外。以后你就会发现,这个组合键非常方便!我们看着很炫的游戏和图形程序通常都是在一个循环中运行的,这些程序需要不断地从鼠标、键盘或游戏控制器中获得输入,然后对这个输入进行处理,并刷新屏幕。当我们开始编写这种程序时,会大量使用循环。你的某个程序很有可能会在某个时候卡在循环里面,这时候你就可以使用这个快捷键来让程序跳出循环!
你可能已经注意到,循环值的列表是包含在中括号里的,Python 利用中括号以及数字之间的逗号来创建列表,我们会在下一章学习关于列表的知识。目前只需要知道,列表是一种“容器”,用来将一些数据存放在一起。在本例中,这些数据就是数字,也就是每次循环迭代时循环变量Item所取的值。
2、使用for循环
现在就让我们用循环来做点好玩的事情,比如打印一由*组成的直角三角形,形状如图2-2所示:
图2-2 由*构成的直角三角形
这个图形看似复杂,其实只需对程序只对前面的程序ch2_7_4_1for.py稍微做修改即可打印出来,新建ch2_7_4_1fortriangle.py文件,输入如下代码:
for item in [1,2,3,4,5,6,7,8,9,10]:
print("* "*item)
看到print("* "*item),估计有人会有人问,这是什么意思,这里稍微解释一下,使用*连接数值类型的时候,表示的是符号两边数值相乘,如果左边是字符串右边是数字,则表示重复左边的字符串多少,"* "*item表示:重复"* "字符串item次。
运行程序,你会在运行结果窗口看到这样的结果:
*
* *
* * *
* * * *
* * * * *
* * * * * *
* * * * * * *
* * * * * * * *
* * * * * * * * *
* * * * * * * * * *
现在估计你已经意识到循环的威力了!如果没有循环结构,想要得到同样的结果,必须这样编写程序:
print("* "*10)
print("* "*9)
print("* "*8)
print("* "*7)
print("* "*6)
print("* "*5)
print("* "*4)
print("* "*3)
print("* "*2)
print("* "*1)
如果要打印一张更长的三角形呢(比如说:从100个倒数、从1000个倒数),不使用循环结构,print语句就会写到你吐,而且在写的过程中,还不能保证会不会漏写或者写错一个数字。但是如果使用循环结构,程序几乎不变,只要列表中增加更多的数字即可,循环让这个问题变得简单多了!当然这个也不是最好的使用循环结构打印’*’三角形的方法,下面我们就range函数来写更简洁的代码。
3、range()函数
上面的画字符三角形的例子只循环了10次,我们使用列表这么实现:
for item in [1,2,3,4,5,6,7,8,9,10]:
如果想画更大的三角形呢?显然我想循环运行100次、1000次或者更多次,该怎么做呢?那就得键入很多很多的数字!
显然输入这个常常的列表也是个很讨厌和消耗耐心的工作,Python为了解决这种连续整数空间的数字串的问题,提供了range()函数。只需输入起始值和结束值,range() 函数就会帮你创建它们之间的所有值。修改ch2_7_4_1fortriangle.py使用了range()函数。
for item in range(1,10):
print('* '*item)
保存并运行,你会看到这样的结果:
*
* *
* * *
* * * *
* * * * *
* * * * * *
* * * * * * *
* * * * * * * *
* * * * * * * * *
该结果和ch2_7_4_1fortriangle.py的运行结果相比少了最后一行10个*的,这是为什么呢?
答案就在于,range(1,10) 给出的列表是 [1, 2, 3, 4 ,5 ,6 ,7 ,8 ,9 ]。为什么没有10呢?这正是range()函数的运行机制。它会生成一个数字列表,该列表从起始值开始,到结束值的前一个数字为止(不包括结束值)。考虑到这一点,我们可以通过调整数值范围来得到想要的循环次数。
修改ch2_7_4_1fortriangle.py代码,增加如下代码:
for item in range(1,11):
print('* '*item)
运行这个程序,结果如下所示:
*
* *
* * *
* * * *
* * * * *
* * * * * *
* * * * * * *
* * * * * * * *
* * * * * * * * *
* * * * * * * * * *
在增加的代码中,range(1, 11) 会给出一个从数字 1 到数字 10 的列表,循环会依次对应列表中的每个数字完成一次迭代。每次迭代结束后,循环变量item会取出列表中的下一个值。
4、循环变量
循环变量和其他变量是一样的,没有任何特殊之处,只是名字不同而已,这变量也可以看作循环计数器,其命名规则完全遵守Python的变量命名规则,在第一章讲解变量命名的时候我们说过,变量名要能够描述变量的用途。因此,循环变量尽量能够表明其意义比如:looper、counter、index等等作为变量名。不过,也有例外,比如对循环变量我们经常有个默认的编程惯例:使用字母 i、j、k 等作为循环变量名。
由于很多人使用 i、j、k 作为循环变量名,因此开发者在看到循环结构的时候,对 i、j、k 马上就能理解其作用。当然,我们也可以用其他名字命名循环变量,但是,i、j、k 只可以用于命名循环变量,如果你学过其他语言,尤其是C、Java语言,你看到i、j、k 马上就会想到循环结构。
下面我们使用这个惯例来世改写我们画三角形的例子,修改ch2_7_4_1fortriangle.py增加如下代码:
for i in range(1,11):
print('* '*i)
运行结果与之前完全相同,你不妨运行ch2_7_4_1fortriangle.py试试看!
如何给循环变量命名是与具体语言的代码书写标准有关,一般在一门具体的语言中都会有一种或几种约定好的社区标准或者官方标准:常见的驼峰原则,变量意义加类型原则在各类语言中都基本适用,这种具体语言约定好的书写代码的方式我们有时候也称之为代码样式。代码样式(style)只影响到程序的可读性和可理解性,与其功能是否能够正常工作无关。但是,如果你的编程代码样式与其他程序员的代码样式一致,那么你的程序就会更容易阅读和理解,也更容易调试。同时,你会更加习惯这种代码样式,还能够更轻松地读懂其他人编写的程序。
5、range()的简洁使用
使用range()函数的使用,并不一定非要给其提供两个参数(象ch2_7_4_1fortriangle.py),如果我们使用默认起始值作为其开始值的话,可以只提供一个参数:
for i in range(11):
这跟下面的写法是完全相同的:
for i in range(0,11):
这两种写法都会得到数字列表 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]。
实际上,大多数程序员习惯从 0 而不是从 1 开始执行循环。如果使用 range(5),就会得到这个循环的 5 次迭代,这样做很容易记住。但是要知道,在第一次迭代时,i 的值是 0 而不是 1;在最后一次迭代时,i 的值是 4 而不是 5。
为什么大多数程序员习惯从 0 而不是从 1 开始执行循环呢?
早期的程序设计时,有些人支持从 1 开始执行循环,有些人则支持从 0 开始执行循环。针对哪一种做法更好,他们进行了激烈的争论。最终,支持从 0 开始执行循环的人胜出了。所以,如今大多数人会从 0 开始执行循环。不过,你仍然可以根据自己的喜好来选择任意一种做法。但是,记住根据循环变量的初始值相应地调整其上限,这样才能得到正确的迭代次数。
6、关于中文和英语字符串的一个有趣的事实
我们来看一段代码,新建ch2_7_4_1forstr.py,输入如下代码:
for word in "Python数据分析必不可少。":
print(word)
保存,运行,能看到如下结果:
P
y
t
h
o
n
是
数
据
分
析
常
用
语
言
。
运行程序我们看到一个有趣的现象,你已经发现字符串的一些规律了,在代码的循环处理中字符串就是一个字符列表,计数循环使用列表来进行迭代。也就是说,我们也可以使用字符串来实现循环。字符串中的每个字符对应循环中的一次迭代,等等,这里注意中英文的每次迭代取字节的不同,英文的字符取一个字节,而中文字符一次取两个字节,我们第一章讲过。因此,如果把循环变量的值打印出来,就会得到这个字符串中的每一个字符,而且每次只打印一个字符。又因为每条 print 语句都会换行,所以每个字符都会打印在单独的一行上。
7、改变循环步长和方向
到目前为止,我们的计数循环在每次迭代时都会让循环变量加1。如果想让循环按步长为2来计数,该怎么做呢?要让步长为5、10或者其他数字,又该怎么做呢?还有,如何逆向计数呢,也就是如何从大到小的计数呢?
看着好像很难,其实很简单,range()函数是可以接受多个参数的,其中一个参数就是步长,利用步长参数就可以把步长从默认的 1 改为其他值。
因为现在没有讲函数的标准定义,我们先稍微解释一下函数参数的概念。我们可以这么认为参数(argument)就是调用像range()这样的函数时我们放在括号里的值。关于参数还有很多相关概念,这里我们先这么理解,第4章我们会介绍更多关于函数、参数和形参的内容。
新建ch2_7_4_1forstepdireciton.py,我们输入如下代码:
for i in range(0,12,2):
print(i)
运行会在结果窗口看到:
0
2
4
6
8
10
在代码我们向 range() 函数增加了第 3 个参数:2。现在循环就以按步长为 2 来计数。在输入如下代码:
for i in range(7,29,6):
print(i)
保存运行,结果窗口看到:
7
13
19
25
这个以步长为6来做循环。
如果想反向计数,那么应该怎么做呢?很简单,步长设置为负数即可,另外能不能以小数作为步长呢?答案是不能,python标准的range()函数只能以整数为步长,在ch2_7_4_1forstepdireciton.py添加如下代码:
for step in range(6,0,-1):
print(step)
for step in range(1.0,4.0,0.5):
print(step)
运行,结果窗口显示如下:
6
5
4
3
2
1
Traceback (most recent call last):
File "/home/oliver/PycharmProjects/pythonProject1/ch2_7_4_1forstepdireciton.py", line 9, in <module>
forstep in range(1.0,4.0,0.5):
TypeError: 'float' object cannot be interpreted as an integer
当 range() 函数中的第 3 个参数是负数时,循环就会向下计数,而不是向上计数。你应该还记得,循环会从起始值开始,向上增加或向下减少,直至达到(但不包括)结束值,所以在上一个例子中,我们只能向下计数到 1,而不是 0。
做后我们看到错误信息:TypeError: 'float' object cannot be interpreted as an integer,表明range函数的第三个参数只能接受整数,不能接收小数。
我们可以尝试来一个好玩的东西,最近这几年中国航天发射非常频繁,我我们经常看到火箭发射的最后倒计时,我们尝试着永我们刚学到的循环来实现这个倒计时过程,创建ch2_7_4_1forboost.py几行代码即可实现这个过程:
import time
print("火箭发射最后10秒倒计时:")
for i in range(10,0,-1):
print(i)
time.sleep(1)
print("点火!")
运行代码,可以在运行结果窗口看到动态的计时过程,是不是有点心动的感觉。哈哈,心动完了,然后就是咦,import、time、sleep是啥玩意,这个后面我们详细讲解,这里简单的说明一下,import语句就是引入Python的类库的关键字,相当于我们过去学习C语言的include的指令,time是Python的标准模块,sleep是time模块提供的休眠函数,让程序暂停指定的秒数。
这里的关键是 range(10, 0, -1),它会让循环从 10 反向计数到 1。
8、没有数字出现的计算循环
在前面所有的例子中,循环变量都是一个数字。用编程术语来讲就是:循环在一个数字列表上进行迭代,那么看到现在估计就有很多人疑问了,不能在非数字的数据上做循环吗?如果不能显然Python太逊了,肯定回答你们,能,for结构的列表里面可以可以是任意的数据类型,后面会能看到更多更复杂的数据类型出现的for结构的范围列表中,最简单就是是字符列表(字符串),这种情形上面ch2_7_4_1forstr.py代码里演示过,下面我在演示一个字符串列表的例子,创建ch2_7_4_1forstrlist.py,输入如下代码,我们使用循环打印大学城有几所高校:
print("大学城里面的高校有:")
for college in ["安徽财经大学", "蚌埠医科大学", "蚌埠学院",
"安徽科技学院","安徽电子信息技术职业学院"]:
print(college, "位于大学城内")
运行程序,在结果窗口能看到:
大学城里面的高校有:
安徽财经大学 位于大学城内
蚌埠医科大学 位于大学城内
蚌埠学院 位于大学城内
安徽科技学院 位于大学城内
安徽电子信息技术职业学院 位于大学城内
现在,我们迭代循环的不再是数字列表,而是字符串列表,而且使用 college而不是i作为循环变量。每次迭代后,循环变量college都会取出列表中的下一个值。尽管这个列表不是数字列表,但从本质上讲,这仍是一种计数循环,Python 仍要计算列表中共有多少项元素来确定循环次数。
可是,如果无法提前知道需要迭代多少次,该怎么办呢?如果没有可用或合适的列表,又该怎么办呢?别着急,接下来就会讲到。
9、条件循环—while 循环
我们前面学习了for循环(计数循环)。如果无法提前知道需要迭代多少次或者没有可用或合适的列表,我们这个时候需要第二种循环结构:while 循环(条件循环)。
提前知道循环需要运行多少次,那么用for循环就很合适。不过,有时候你可能想让循环一直运行下去,直到某种条件发生时才结束,但你并不知道在这种情况发生之前会有多少次迭代,这时就可以使用while循环来实现。
在本章前面,我们了解了条件和判断的概念,还学习了if语句。while 循环无法计算需要执行多少次迭代,但可以通过其后的条件来确定什么时候停止循环。从这个角度,while 循环有时间也称为条件循环。在某个条件满足时,while 循环会一直执行下去。
利用while循环语句,可以让一个语句块一遍又一遍地执行。只要while循环语句的条件为True,while子句中的代码就会执行。在代码中,while循环语句总是包含以下几部分:
- while关键字。
- 条件(求值为True或False的表达式)。
- 冒号。
- 从下一行开始,缩进的语句块(称为while子句)。
可以看到,while循环语句看起来和if语句类似。不同之处是它们的行为。if子句结束时,程序继续执行if语句之后的语句。但在while子句结束时,程序跳回到while循环语句开始处执行。while子句常被称为“while循环”。
简单地说,while 循环会一直询问“完成了吗……完成了吗……完成了吗……”,直到所给条件不再为真时,循环才结束。
说了这么多while到底怎么用估计还是一头雾水,当然你学过其他语言的好说,新建ch2_7_4_1while.py,输入如下代码:
print("请输入正确的用户和密码以退出程序!")
userName = input("请输入用户名:")
userPassword=input("请输入用户密码:")
while userName != 'oliver' or userPassword!='123456':
if userName != 'oliver':
print("用户名不正确!")
if userPassword!='123456':
print("用户密码不正确!")
userName = input("请输入用户名:")
userPassword=input("请输入用户密码:")
print("程序也结束!")
这个程序会不停地要求用户输入用户名和密码,只要输入的userName不是字符串'oliver'或者userPassword不是字符串'123456'的时候,while的条件就为 True,程序进入while子句,查看输入userName和userPassword那个不正确,给出错误提示信息,再次要求用户输入userName和userPassword。只有当输入的userName是'oliver'和userPassword是'123456'的时候,while条件才为 False,循环停止,接着执行while结构后面的语句,打印程序结束。你可以尝试运行这个程序,输入错误的用户名和密码,输入正确的用户名和密码会看到这样的运算结果:
请输入正确的用户和密码以退出程序!
请输入用户名:oliver
请输入用户密码:1232
用户密码不正确!
请输入用户名:o
请输入用户密码:123
用户名不正确!
用户密码不正确!
请输入用户名:oliver
请输入用户密码:123456
程序也结束!
10、 提前跳转—continue语句
有时候,你可能想提前结束循环,比如使for循环中断计数,或者使while循环停止判断条件。要提前结束循环,可以采用两种方法:用continue语句直接跳到循环的下一次迭代,或者用break语句彻底终止循环。下面来详细说明。
如果想停止当前的迭代循环,提前跳到下一次迭代循环,那么可以使用continue语句。我们还是用例子来说明continue如何使用,新建ch2_7_4_1forcontinue.py,输入如下代码:
for i in range(1, 5):
print()
print('第', i, '次问候 ', end='')
print('你好!', end='')
if i == 2:
continue
print('今天还好吧?', end='')
print()
这个程序的运行结果如下所示:
第 1 次问候 你好!今天还好吧?
第 2 次问候 你好!
第 3 次问候 你好!今天还好吧?
第 4 次问候 你好!今天还好吧?
注意,当第2次循环时(i ==2),循环体并没有结束,而是提前跳到了下一次迭代(i ==3),这就是continue语句的作用。在while循环中,continue语句的作用也是一样的。
11、跳出循环—break 语句
如果想彻底跳出循环,不再完成循环计数,或者不再判断循环条件,应该怎么做呢?此时可以使用break语句。
创建ch2_7_4_1forbreak.py,这个程序就是把把代码ch2_7_4_1forcontinue.py 中第6行的continue语句换成break语句:
for i in range(1, 5):
print()
print('第', i, '次问候 ', end='')
print('你好!', end='')
if i == 2:
break
print('今天还好吧?', end='')
print()
运行程序,你会在结果窗口看到如下内容:
第 1 次问候 你好!今天还好吧?
第 2 次问候 你好!
这一次不是跳过了第2次循环中的后续语句,而是彻底终止了循环,这正是break语句的作用。在while循环中,break 语句的作用也是一样的。
需要指出的是,有些人认为使用continue语句和break语句并不好。我个人不认同这种观点,不过我确实很少用到这两条语句。不管怎样,现在你已经知道了continue语句和break语句的用法,没准以后会用得到。
2.7.3 《一锤定音》最后猜价格过程模拟
《一槌定音》是央视财经频道的一个栏目,该栏目以模拟真实艺术品买卖为主体形式,一共两个环节,第一个环节是通过判断古董的真伪进行积分,最后累计总分的前三名进入最后第二个环节,第二个环节是猜最后一件藏品的价格,一共6次猜价格的机会,第一环节的第一名有三次竞猜的机会,第二名有两次,第三名有一次,在竞价的过程中,三个选手狗通过按面前的抢答器进行竞抢,每次只有抢中的人才能给出价格,主持人根据当前选手给出的价格与藏品价格的比较,给出当前价格比藏品实际价格高了还是低了的提示,然后进入下一轮猜价格,当选手手里的猜价次数用完之后就不能再参与竞价,在竞价过程中如果有人猜中价格,竞价结束,该选手获得最后胜利,如果在六轮中没人猜中价格,最后会根据3个选手最后给出的价格与藏品价格接近程度判定谁是最后的胜利者,差距最小的获胜,如果相同,则共享最后的大奖。
下面我们用刚刚学过的两种基本程序结构来模拟这个过程,在模拟过程中会用到LIST的相关知识,这个下一章会有详细讲解,这里会简单解释一下所用函数,新建ch2_7_3antiquePrice.py,输入如下代码:
import random
antiquePrice = random.randint(1, 100) #随机产生一个藏品的价格
guess1 = 0 #1号选手的最后竞价
guess2 = 0 #2号选手的最后竞价
guess3 = 0 #3号选手的最后竞价
guess = 0 # 当前竞价
tries = 0 # 竞价轮数
firstPerson = 3 # 第一名有的出价机会
secondPerson = 2 # 第二名有的出价机会
thirdPerson = 1 # 第三名有的出价机会
person = [1, 2, 3]
print("请三位投资人竞猜XXXX藏品的价格!")
print("这件藏品是XXXX,巴拉巴拉一堆宝物介绍!")
print("根据前面积分的获取的名次,第一名友三次出价的机会")
print("第二名有两次出价的机会,第三名有一次出价机会")
print("下面请各位出价:")
while guess != antiquePrice and tries < 6: # 如果没有猜中价格和不满6轮,继续竞价
print("-"*10+"第" + str(tries + 1) + "竞价"+"-"*10)
p = random.choice(person) # 使用随机模拟 竞价人抢竞
print("-"*5+str(p)+"号选手按下竞价器!")
if p == 1:
firstPerson = firstPerson - 1
guess1 = int(input("请1号输入价格: "))
guess = guess1
if firstPerson == 0: #如果没有次数,不能在参与竞价
person.remove(p)
elif p == 2:
secondPerson = secondPerson - 1
guess2 = int(input("请2号输入价格: "))
guess = guess2
if secondPerson == 0: #如果没有次数,不能在参与竞价
person.remove(p)
else:
thirdPerson = thirdPerson - 1
guess3 = int(input("请3号输入价格: "))
guess = guess