经过第一讲的学习很多读者已经能用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 = guess3
person.remove(p) #第三名就一次机会,出价后不能在竞价
if guess < antiquePrice: #主持人根据当前价格与藏品价格比较给出提示
print("价格低了,请各位再次输入价格!")
elif guess > antiquePrice:
print("价格高了,请各位在次输入价格!")
tries = tries + 1
if guess == antiquePrice:
if guess1 == guess:
print('恭喜1号选手胜出,获得最后大奖!')
elif guess2 == guess:
print('恭喜2号选手胜出,获得最后大奖!')
else:
print('恭喜3号选手胜出,获得最后大奖!')
else:
print("根据拍卖行给出的参考价为:", antiquePrice)
a = [abs(antiquePrice - guess1), abs(antiquePrice - guess2), abs(antiquePrice - guess3)]
print("三位投资人给出的价格和差价分别是")
print("1号:" + str(guess1) + " 差价:" + str(antiquePrice - guess1))
print("2号:" + str(guess2) + " 差价:" + str(antiquePrice - guess2))
print("3号:" + str(guess3) + " 差价:" + str(antiquePrice - guess3))
minDisparity = min(a)
for i in range(len(a)):
if minDisparity == a[i]:
print("恭喜" + str(i + 1) + "选手获胜,赢取本次一锤定音大奖")
这里解释一下用到的几个函数:
antiquePrice = random.randint(1, 100):随机产生一个1-100之间的整数。
p = random.choice(person):从person列表中随机选取一个值。
person.remove(p):从列表中删除值为p元素。
abs():取一个数的绝对值。
min(a):取一个list中的最小值。
运行程序,在结果输出窗口竞价过程如下:
请三位投资人竞猜XXXX藏品的价格!
这件藏品是XXXX,巴拉巴拉一堆宝物介绍!
根据前面积分的获取的名次,第一名友三次出价的机会
第二名有两次出价的机会,第三名有一次出价机会
下面请各位出价:
----------第1轮竞价----------
-----1号选手按下竞价器!
请1号输入价格: 70
价格高了,请各位在次输入价格!
----------第2轮竞价----------
-----1号选手按下竞价器!
请1号输入价格: 35
价格低了,请各位再次输入价格!
----------第3轮竞价----------
-----2号选手按下竞价器!
请2号输入价格: 55
价格高了,请各位在次输入价格!
----------第4轮竞价----------
-----3号选手按下竞价器!
请3号输入价格: 45
价格低了,请各位再次输入价格!
----------第5轮竞价----------
-----2号选手按下竞价器!
请2号输入价格: 50
价格低了,请各位再次输入价格!
----------第6轮竞价----------
-----1号选手按下竞价器!
请1号输入价格: 52
价格低了,请各位再次输入价格!
根据拍卖行给出的参考价为: 53
三位投资人给出的价格和差价分别是
1号:52 差价:1
2号:50 差价:3
3号:45 差价:8
恭喜1选手获胜,赢取本次一锤定音大奖
2.7.4 嵌套循环与可变循环
通过上面模拟竞价过程我们能看到,在循环体(语句块)中可以包含其他程序结构,可以是简单的顺序代码,也可以是简单的分支结构,还可以是复杂的分支结构等等。仔细分析竞价的过程,你很会发现,最外层是一个while循环,在while循环内首先是简单的顺序语句,接着就是if块、elif 块和else语句块,然后接着又是另一个if块和elif 块,接着循环结束。看到这里估计很多人都会有个疑惑,在循环结构里面能放顺序结构和分支结构,那么能不能在循环内放另一个循环呢?显然没问题,我们还可以把一个循环放在另一个循环内,这样的循环叫作嵌套循环。
写道这里突然想起那个3×5好难的视频,视频里面一个小姑娘被家长逼着被乘法口诀,3乘5总是出错,我当时看着一脸的心疼,乘法口诀和乘法表对每个启蒙的孩子来说都有可以记忆一辈子的故事,小时候买的文具盒子上都会印有一个乘法口诀表,那么现在就有个问题了,如果我们用Python来打印一个乘法口诀表,我们应该怎么实现呢?乘法表的样子如图2-2
图2-2 乘法口诀表
我们尝试着用学过的循环结构来实现,首先写一个6的乘法序列,第一反应的代码肯定这样,创建ch2_7_4_1multiply.py,输入如下代码:
seed = 6
for i in range (1, 7):
print(str(i)+"×"+str(seed), "=", i * seed)
保存,运行,在结果窗口我们看到如下形式的数据:
1×6 = 6
2×6 = 12
3×6 = 18
4×6 = 24
5×6 = 30
6×6 = 36
结果和乘法口诀表的6的样式不一样啊?这怎么办?问题处在什么地方?问题处在我们没有正确使用print函数。我们打开print()函数,其标准定义格式如下:
def print(self, *args, sep=' ', end='\n', file=None): # known special case of print
"""
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file: a file-like object (stream); defaults to the current sys.stdout.
sep: string inserted between values, default a space.
end: string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
"""
pass
注意看它的参数,默认的使用格式:print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False),向标准输出打印不固定数量个value,value之间的分割是空格,默认的打印结束符号是'\n',显然每次运行print函数都会另起一行,为了能打印出乘法口诀表的样式,我们改变end参数值即可,下面根据这个思路进行改进,修改ch2_7_4_1multiply.py代码,注释掉上一部分代码,观察乘法口诀表,显然打印的时候seed应该放在前面,默认的sep是空格,现在为了更符合乘法口诀表的样式,设置sep=’’,添加如下代码:
seed = 6
for i in range (1, 7):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
保存运行代码,结果窗口看到:
1×6 = 6
2×6 = 12
3×6 = 18
4×6 = 24
5×6 = 30
6×6 = 36
6×1=6 6×2=12 6×3=18 6×4=24 6×5=30 6×6=36
显然我们打印的这一行和乘法口诀表的第六行样式完全一致了,接着我们要做的就是想办法从1到9分别把每个数字这个形式都打印一边,显然直觉得套路就是修改ch2_7_4_1multiply.py代码,增加1到9的打印循环,代码如下:
seed = 1
for i in range (1, 2):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
seed = 2
for i in range (1, 3):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
seed = 3
for i in range (1, 4):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
seed = 4
for i in range (1, 5):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
seed = 5
for i in range (1, 6):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
seed = 6
for i in range (1, 7):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
seed = 7
for i in range (1, 8):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
seed = 8
for i in range (1, 9):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
seed = 9
for i in range (1, 10):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
保存运行,结果如下:
1×1=1 2×1=2 2×2=4 3×1=3 3×2=6 3×3=9 4×1=4 4×2=8 4×3=12 4×4=16 5×1=5 5×2=10 5×3=15 5×4=20 5×5=25 6×1=6 6×2=12 6×3=18 6×4=24 6×5=30 6×6=36 7×1=7 7×2=14 7×3=21 7×4=28 7×5=35 7×6=42 7×7=49 8×1=8 8×2=16 8×3=24 8×4=32 8×5=40 8×6=48 8×7=56 8×8=64 9×1=9 9×2=18 9×3=27 9×4=36 9×5=45 9×6=54 9×7=63 9×8=72 9×9=81
Process finished with exit code 0
打印在一行了,现在我们需要将每个数字那一行分开,怎么办?方法很简单,前面看print()函数的时候,其默认结束符就是换行,所以我们在打印每个数字乘法行之前调用一个空的print()即可,修改代码:
seed = 1
for i in range (1, 2):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 2
for i in range (1, 3):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 3
for i in range (1, 4):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 4
for i in range (1, 5):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 5
for i in range (1, 6):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 6
for i in range (1, 7):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 7
for i in range (1, 8):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 8
for i in range (1, 9):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 9
for i in range (1, 10):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
保存运行,得到结果:
1×1=1
2×1=2 2×2=4
3×1=3 3×2=6 3×3=9
4×1=4 4×2=8 4×3=12 4×4=16
5×1=5 5×2=10 5×3=15 5×4=20 5×5=25
6×1=6 6×2=12 6×3=18 6×4=24 6×5=30 6×6=36
7×1=7 7×2=14 7×3=21 7×4=28 7×5=35 7×6=42 7×7=49
8×1=8 8×2=16 8×3=24 8×4=32 8×5=40 8×6=48 8×7=56 8×8=64
9×1=9 9×2=18 9×3=27 9×4=36 9×5=45 9×6=54 9×7=63 9×8=72 9×9=81
咦,一样了啊,但是我们那个代码?观察打印每一行的那个循环的上边界,很自然的就发现for循环中range()函数的上限都比seed大1,那么这个时候我们可以把每个打印行中的for循环的range()函数都改写成range(1,seed+1):
seed = 1
for i in range (1, seed+1):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 2
for i in range (1, seed+1):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 3
for i in range (1, seed+1):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 4
for i in range (1, seed+1):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 5
for i in range (1, 6):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 6
for i in range (1, seed+1):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 7
for i in range (1, seed+1):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 8
for i in range (1, seed+1):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
print()
seed = 9
for i in range (1, seed+1):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
在分析进化后的代码,如下代码,重复的出现了9次,
for i in range (1, seed+1):
print(str(seed)+"×"+str(i), "=", i * seed,sep='',end=' ')
观察seed变化规律,好像是从1到9咦,每次变化都是增加1,我们也可以使用过的循环结构循环产生这个数,代码如下:
for seed in range(1,10):
接下来我们就把刚那段频繁出现的代码放在这个循环的循环体里面:
for seed in range(1,10):
for i in range(1,seed+1):
print(str(seed)+"×"+str(i)+'=',seed*i,' ',end='')
print()
保存运行,最后得到同样的结果。
这种把一个循环包含在另一个循环的的循环体的方法我们称之为嵌套循环,本质就是一个循环内包含另一个循环,对于外循环的每一次迭代,内循环都要完成它的所有迭代。
问题又来了,循环只能嵌套两层吗?不是,可以无限嵌套下去,可以根据自己编写代码的需要增加嵌套的层次,另外,正如分支结构可以出现在循环体里面一样,循环结构也可以出现在分支结构的语句块中。
还有另一个方面我们要说明,就是循环的边界,比如上面使用的for循环,range()函数的下边界和上边界和步长:range(a,b,step),都可以使用表达式来替换,这就造成在循环过重中,循环次数的可变的,这种循环次数随着参数值不同动态变化循环次数的循环称之为可变循环,上面打印乘法口诀range函数的上边界就用了表达式表示,随着seed的变换,内循环的次数就是动态变化的。
可变循环也可以在另一个可变循环的循环体里面出现,这就是可变循环嵌套,本质上静态的循环嵌套一样,唯一区别仅仅是循环的次数随着程序的演进变化而已。
2.8 使用系统标准库和第三方库
在上面讲解程序结构的时候,其实已经涉及到了Python的标准函数和标准库,这些标准函数这称为“内置函数”,包括上面用到的print()、input()和len()函数。Python还包括一组模块,称为“标准库”,每个模块都是一个Python程序,其一组相关的函数,可以嵌入你的程序之中。例如,math模块有与数学运算相关的函数,random模块有与随机数相关的函数等。
使用标准函数的时候,可以直接调用,但如果使用Python标准库中的函数时,在函数之前,必须用import语句导入该模块。在代码中,import语句包含以下部分。
- import关键字。
- 模块的名称。
- 可选的更多模块名称,之间用逗号隔开。
在导入一个模块后,就可以使用该模块中所有的函数。比如我们前面使用过的random模块,导入random模块之后,我们就可以使用random.randint()函数来产生一个指定范围的内的随机整数,当然random模块内的函数不仅仅是randint(),还有sample(),shuffle(),choices()。创建ch2_8randomint.py输入以下代码:
import random
print("随机从[1..99]的列表中取5个样本,取3组")
for i in range(3):
print(random.sample(range(1,100),k=5))
print("随机从[1...9]列表中取一个数,取10次")
for i in range(10):
print(random.randint(1,10),sep='',end=' ')
print()
print("随机从[3...14]列表中取一个数,取10次")
for i in range(10):
print(random.choice(range(2,15)),sep='',end=' ')
保存Python脚本时,请不要将它们命名为Python模块已经使用的名称,例如random.py、sys.py、os.py或math.py。如果不小心将其中一个程序命名为random.py,并在另一个程序中使用import random语句,那么程序将导入你的random.py文件,而不是Python的random模块。这可能导致错误,例如AttributeError: module 'random' has no attribute 'randint',因为你的random.py没有真正的random模块具有的函数。也不要使用任何Python内置函数的名称,例如print()或input()。
诸如此类的问题很少见,但解决起来却很棘手。随着编程经验的增加,你会更加了解Python模块和函数使用的标准名称,遇到这些问题的频率会降低。
运行这个程序,输出看起来可能像下面这样,但不会完全相同:
随机从[1..99]的列表中取5个样本,取3组
[58, 71, 96, 11, 84]
[82, 22, 91, 25, 74]
[84, 18, 68, 85, 58]
随机从[1...9]列表中取一个数,取10次
6 10 6 8 10 7 8 5 6 7
随机从[3...14]列表中取一个数,取10次
13 11 2 5 3 4 10 3 6 12
random. randint()函数调用求值为传递给它的两个整数之间的一个随机整数。上面用到的randint()等函数属于random模块,所以必须在函数名称之前先加上random.,告诉Python在random模块中寻找这个函数。
下面是import语句的例子,它导入了4个不同的模块:
import random, sys, os, math
引入模块之后,我们就可以在代码里使用这4个模块中的所有函数了。
from import语句
import语句的另一种形式包括from关键字,之后是模块名称、import关键字和一个星号,例如from random import *。
使用这种形式的import语句,调用random模块中的函数时不需要random.前缀。但是,使用完整的名称会让代码更具可读性,所以建议使用普通形式的import语句。
2.9 结束程序函数:sys.exit()
最后一个控制结构的概念是如何结束程序。正常程序当执行完最后一条指令时,程序就会终止。但是,如果你想在代码中提前退出程序,或者是特定条件下退出程序,就要调用sys.exit()函数了,这个函数在sys模块中,使用它之前必须先导入sys。
看到这里估计读者会疑惑,到目前位置我们写的代码都是自动结束啊,不需要中退出操作,这个函数的适用的业务场景,我们后面写小游戏的时候会用到,这里我们就简单通过一个小例子说明一下其用法,创建ch2_9exit.py输入以下代码:
import sys
while True:
print('不能结束的循环,如果想退出,请输入quit指令,不区分大小写。')
command = input('请输入你的指令:')
if command.upper() == 'QUIT':
print('您输入的指令是:' + command + '。程序将退出!')
sys.exit()
print('您输入的指令是:' + command + '。程序将继续。')
运行程序,在结果窗口能根据提示输入对应指令:
不能结束的循环,如果想退出,请输入quit指令,不区分大小写。
请输入你的指令:list
您输入的指令是:list。程序将继续。
不能结束的循环,如果想退出,请输入quit指令,不区分大小写。
请输入你的指令:中文指令
您输入的指令是:中文指令。程序将继续。
不能结束的循环,如果想退出,请输入quit指令,不区分大小写。
请输入你的指令:quit
您输入的指令是:quit。程序将退出!
这个程序有一个无限循环,里面没有break语句,结束该程序的唯一方式就是用户输入quit,调用sys.exit()函数。如果command转成大写后等于QUIT,程序就退出。因为command变量由input()函数输入,所以用户只有输入quit才能结束这个程序。
2.10 综合例子:行酒令-划拳
喝酒划拳助兴的游戏,起源于汉代。作为中国酒桌文化,以它独有的风格流传至今。它增添酒兴,烘托喜庆,是汉族民间“酒令”的一种。一般酒过三巡 后就是开始啦!划拳需要很强的技巧性,因玩时须大声喊叫,情绪高昂易让人兴奋,极富挑战性。在民间特别是农村地区比较普及,哥几个在一块,喝个小酒划个拳,日子快活又逍遥。下面我们就利用前面学到的编程概念,创建一个简单的简单的人机划拳游戏。
酒令划拳规则:
1、赢拳
两人同时伸出手指数字相加,和两人谁喊的口诀一样,谁就赢了,输的人需要喝酒。注意:每个人手指伸出来必须是5以内的数字,嘴里喊出来的必须是1到10以内的。
2、臭拳
当自己喊出的数字和自己出的数字相差大于5的时候,此为臭拳,因为人手最多5个手指,你喊的和你出的数字差大于5,对方无论如何也不能出多余个手指,出臭拳者显然已经放弃了赢的机会,这个不判胜负。想想我们喝酒行拳的时候,如果这个喊法的旁边人会笑你,其实就是提醒你,这个时候我们可以打印个提示。
3、高拳
就是连续两次喊数字5,因为5是最安全的数字,无论如何也不会出现“臭拳”,所以为了防止放弃拼搏,选择安逸的结果,5是不允许连续喊,连续两次喊5则为“高拳”。 出高拳者判负,喝酒
- 双方的数字相同,直接进入下一拳。
我们根据规则创建ch2_10finger_guessing_game.py,输入代码,代码的作用在都有文字的注释,这里我们不详细解释了:
import sys
import random
total_wine = 1000 # 有一瓶酒
perCup = 200 # 每杯能桩200毫升,这是个大杯,一杯倒
partyAShowNum = 0 # 甲喊出的拳数之和
partyBShowNum = 0 # 乙喊出的拳数之和
lastpartyAShowNum = 0 # 上一次甲喊出的拳数之和
lastpartyBShowNum = 0 # 上一次乙喊出的拳数之和
partAlostCount = 0
partBlostCount = 0
i = 0
while total_wine > 0:
i = i + 1
print("-" * 10, "第", i, "轮模拟猜拳", "-" * 10)
partyAShowNum = int(input("你预测的和值(0-10):"))
partyBShowNum = random.randint(0, 10)
partARealNum = random.randint(0, 5)
partBRealNum = random.randint(0, 5)
print("你预测的和值:", partyAShowNum)
print('电脑喊出的数:', partyBShowNum)
print()
print('你出的手指数:', partARealNum)
print('电脑模拟的数:', partBRealNum)
print("实际数的总和:", partARealNum + partBRealNum)
print()
if (partyBShowNum == 5 and partyBShowNum == lastpartyBShowNum):
partBlostCount = partBlostCount + 1
total_wine = total_wine - perCup
print("电脑连续出5数字,出了高拳,违反规则,判负!,电脑输了,喝酒!")
if (partyAShowNum == 5 and partyAShowNum == lastpartyAShowNum):
partAlostCount = partAlostCount + 1
total_wine = total_wine - perCup
print("你连续出5数字,出了高拳,违反规则,判负!,你输了,喝酒!")
if ((partyBShowNum == 5 and partyBShowNum == lastpartyBShowNum) or (
partyAShowNum == 5 and partyAShowNum == lastpartyAShowNum)):
lastpartyAShowNum = partyAShowNum
lastpartyBShowNum = partyBShowNum
continue
if (partyBShowNum - partBRealNum > 5 and partyAShowNum - partARealNum > 5) or (partyBShowNum == partyAShowNum ):
lastpartyAShowNum = partyAShowNum
lastpartyBShowNum = partyBShowNum
continue
# 喊出的数和出的手指的个数相差大于5显然不可能有胜负的,两个人预测的都对,显然也没胜负,
print()
if (partyBShowNum<partBRealNum):
print("电脑喊出的是:", partyBShowNum, "电脑实际出了手指数为:", partBRealNum,
"出的手指数大于你喊的数,这是酒没喝够,哈哈!")
if (partyAShowNum<partARealNum):
print("你嘴喊出的是:", partyAShowNum, "但你实际出了手指数为:", partARealNum,
"出的手指数大于你喊的数,这是酒没喝够,哈哈!")
if (partyBShowNum - partBRealNum > 5):
print("电脑喊出的是:", partyBShowNum, "电脑实际出了手指数为:", partBRealNum,
"差值大于5,电脑模拟人单手只有5个手指,电脑出臭拳,这是酒没喝够,哈哈!")
if (partyAShowNum - partARealNum > 5):
print("你嘴喊出的是:", partyAShowNum, "但你实际出了手指数为:", partARealNum,
"差值大于5,人单手只有5个手指,你出臭拳,这是酒没喝够,哈哈!")
if (partyAShowNum == partARealNum + partBRealNum):
# 你预测对了数值,对方输了喝酒
partBlostCount = partBlostCount + 1
total_wine = total_wine - perCup
print("电脑输了,喝酒!")
if (partyBShowNum == partARealNum + partBRealNum):
# 对方预测对了数值,你输了,喝酒
partAlostCount = partAlostCount + 1
total_wine = total_wine - perCup
print("你输了,喝酒!")
# 记录本次两人喊的数,用于判断高拳
lastpartyAShowNum = partyAShowNum
lastpartyBShowNum = partyBShowNum
terminateWine = input("提前结束酒局吗?Y/N:")
if terminateWine.upper() == "Y":
sys.exit(0)
print("你输了:", partAlostCount, "次,共喝了:", partAlostCount * perCup, "毫升酒")
print("电脑输了:", partBlostCount, "次,共喝了:", partBlostCount * perCup, "毫升酒")
我们从头开始逐行查看这段代码:
import sys
import random
total_wine = 1000 # 有一瓶酒
perCup = 200 # 每杯能桩200毫升,这是个大杯,一杯倒
partyAShowNum = 0 # 甲喊出的拳数之和
partyBShowNum = 0 # 乙喊出的拳数之和
lastpartyAShowNum = 0 # 上一次甲喊出的拳数之和
lastpartyBShowNum = 0 # 上一次乙喊出的拳数之和
partAlostCount = 0 # 记录人这一方失败的次数
partBlostCount = 0 # 记录计算机那一方失败的次数
i = 0 # 划拳的轮数
首先,导入random和sys模块,以便程序可以调用random.randint()和sys.exit()函数。我们还设置了8个变量来跟踪划拳的进程,变量的作用参看变量后面的说明,接着我们进入划拳的过程:
while total_wine > 0:
i = i + 1
print("-" * 10, "第", i, "轮模拟猜拳", "-" * 10)
partyAShowNum = int(input("你预测的和值(0-10):")) # 模拟人喊拳
partyBShowNum = random.randint(0, 10) # 模拟电脑喊拳
partARealNum = random.randint(0, 5) # 模拟人出拳
partBRealNum = random.randint(0, 5) # 模电脑人出拳
print("你预测的和值:", partyAShowNum) #打印出来
print('电脑喊出的数:', partyBShowNum)
print()
print('你出的手指数:', partARealNum)
print('电脑模拟的数:', partBRealNum)
print("实际数的总和:", partARealNum + partBRealNum) #打印出拳的实际总和
print()
接着我们进入换拳规则的翻译,首判断两者是不是连续喊5,出高拳,有人出高拳,判负,罚酒,两人都是出高拳,同罚,记录本次喊的数字,进入下一轮猜拳:
if (partyBShowNum == 5 and partyBShowNum == lastpartyBShowNum):
partBlostCount = partBlostCount + 1
total_wine = total_wine - perCup
print("电脑连续出5数字,出了高拳,违反规则,判负!,电脑输了,喝酒!")
if (partyAShowNum == 5 and partyAShowNum == lastpartyAShowNum):
partAlostCount = partAlostCount + 1
total_wine = total_wine - perCup
print("你连续出5数字,出了高拳,违反规则,判负!,你输了,喝酒!")
if ((partyBShowNum == 5 and partyBShowNum == lastpartyBShowNum) or (
partyAShowNum == 5 and partyAShowNum == lastpartyAShowNum)):
lastpartyAShowNum = partyAShowNum
lastpartyBShowNum = partyBShowNum
continue
或者两个人喊的数字是不是相同,出臭拳,和喊相同的数字,显然就无法决出胜负:
if (partyBShowNum - partBRealNum > 5 and partyAShowNum - partARealNum > 5) or (partyBShowNum == partyAShowNum ):
lastpartyAShowNum = partyAShowNum
lastpartyBShowNum = partyBShowNum
continue
# 喊出的数和出的手指的个数相差大于5显然不可能有胜负的,两个人预测的都对,显然也没胜负,
接着检查两个人中谁的出拳的手指数大于喊的数,喊的数和出的手指数差额是不是大于5,大于5也是臭拳,正常划拳的时候都会被嘲笑的,代码中也应该给提示:
if (partyBShowNum<partBRealNum):
print("电脑喊出的是:", partyBShowNum, "电脑实际出了手指数为:", partBRealNum,
"出的手指数大于你喊的数,这是酒没喝够,哈哈!")
if (partyAShowNum<partARealNum):
print("你嘴喊出的是:", partyAShowNum, "但你实际出了手指数为:", partARealNum,
"出的手指数大于你喊的数,这是酒没喝够,哈哈!")
if (partyBShowNum - partBRealNum > 5):
print("电脑喊出的是:", partyBShowNum, "电脑实际出了手指数为:", partBRealNum,
"差值大于5,电脑模拟人单手只有5个手指,电脑出臭拳,这是酒没喝够,哈哈!")
if (partyAShowNum - partARealNum > 5):
print("你嘴喊出的是:", partyAShowNum, "但你实际出了手指数为:", partARealNum,
"差值大于5,人单手只有5个手指,你出臭拳,这是酒没喝够,哈哈!")
接着就是判对谁预测正确,罚对方的酒,记录本次喊出的数字,询问是不是结束划拳,每次出现罚酒都会把总酒量消耗一部分,如果继续,跳回循环开始处,检查瓶中的就是不是已经喝完 total_wine > 0是否成立:
if (partyAShowNum == partARealNum + partBRealNum):
# 你预测对了数值,对方输了喝酒
partBlostCount = partBlostCount + 1
total_wine = total_wine - perCup
print("电脑输了,喝酒!")
if (partyBShowNum == partARealNum + partBRealNum):
# 对方预测对了数值,你输了,喝酒
partAlostCount = partAlostCount + 1
total_wine = total_wine - perCup
print("你输了,喝酒!")
# 记录本次两人喊的数,用于判断高拳
lastpartyAShowNum = partyAShowNum
lastpartyBShowNum = partyBShowNum
terminateWine = input("提前结束酒局吗?Y/N:")
if terminateWine.upper() == "Y":
sys.exit(0)
酒喝完之后,打印双发的输赢情况:
print("你输了:", partAlostCount, "次,共喝了:", partAlostCount * perCup, "毫升酒")
print("电脑输了:", partBlostCount, "次,共喝了:", partBlostCount * perCup, "毫升酒")
运行程序,在运行结果窗口能看到整个模拟划拳的过程,这个过程带有随机性,每次的过程和轮数都会不一样:
---------- 第 1 轮模拟猜拳 ----------
你预测的和值(0-10):5
你预测的和值: 5
电脑喊出的数: 2
你出的手指数: 2
电脑模拟的数: 0
实际数的总和: 2
你输了,喝酒!
提前结束酒局吗?Y/N:n
---------- 第 2 轮模拟猜拳 ----------
你预测的和值(0-10):6
你预测的和值: 6
电脑喊出的数: 4
你出的手指数: 5
电脑模拟的数: 4
实际数的总和: 9
提前结束酒局吗?Y/N:n
。。。。
---------- 第 12 轮模拟猜拳 ----------
你预测的和值(0-10):6
你预测的和值: 6
电脑喊出的数: 1
你出的手指数: 5
电脑模拟的数: 3
实际数的总和: 8
电脑喊出的是: 1 电脑实际出了手指数为: 3 出的手指数大于你喊的数,这是酒没喝够,哈哈!
提前结束酒局吗?Y/N:n
。。。。。。
---------- 第 20 轮模拟猜拳 ----------
你预测的和值(0-10):5
你预测的和值: 5
电脑喊出的数: 2
你出的手指数: 2
电脑模拟的数: 3
实际数的总和: 5
电脑喊出的是: 2 电脑实际出了手指数为: 3 出的手指数大于你喊的数,这是酒没喝够,哈哈!
电脑输了,喝酒!
提前结束酒局吗?Y/N:n
你输了: 2 次,共喝了: 400 毫升酒
电脑输了: 3 次,共喝了: 600 毫升酒
2.11 小结
通过使用求值为True或False的表达式(也称为条件),你可以编写程序来决定哪些代码执行,哪些代码跳过。只要某个条件求值为True,可以在循环中一遍又一遍地执行代码。如果需要跳出循环或回到开始处,break和continue语句很有用。
这些控制流语句可以让你写出非常智能化的程序。还有另一种类型的控制流,你可以通过编写自己的函数来实现,这是下一章的主题。
2.12 习题
1.布尔数据类型的两个值是什么?如何拼写?
2.3个布尔运算符是什么?
3.写出每个布尔运算符的真值表(也就是操作数的每种可能组合,以及操作的结果)。
4.以下表达式求值的结果是什么?
(5 > 4) and (3 == 5)
not (5 > 4)
(5 > 4) or (3 == 5)
not ((5 > 4) or (3 == 5))
(True and True) and (True == False)
(not False) or (not True)
5.6个比较运算符是什么?
6.等于运算符和赋值运算符的区别是什么?
7.解释什么是条件,可以在哪里使用条件。
8.识别这段代码中的3个语句块:
spam = 0
if spam == 10:
print('eggs')
if spam > 5:
print('bacon')
else:
print('ham')
print('spam')
print('spam')
9.编写代码,如果变量spam中存放1,就输出Hello;如果变量中存放2,就输出Howdy;如果变量中存放其他值,就输出Greetings。
10.如果程序陷在一个无限循环中,你可以按什么键退出循环?
11.break和continue语句之间的区别是什么?
12.在for循环中,range(10)、range(0, 10)和range(0, 10, 1)之间的区别是什么?
13.编写一小段程序,利用for循环输出从1到10的数字。然后利用while循环编写一个等价的程序,输出从1到10的数字。
14.如果在名为spam的模块中,有一个名为bacon()的函数,那么在导入spam模块后,如何调用它?
附加题:在因特网上查找round()和abs()函数,弄清楚它们的作用。在交互式环境中尝试使用它们。
0 条 查看最新 评论
没有评论