第5章灵活的积木 大家以前应该都玩过积木,一块块小的正方体、圆柱体、长方体等积木可以搭建成美丽的城堡。我们如果把一段代码包装起来,起个名字,那么这个代码段就像一块块形状各异的积木,有不一样的功能,我们就把这样一段起了名字的代码叫作函数。函数的使用,在Python中叫作调用,相当于动手搭积木的过程——找出自己想要的积木的形状,知道函数完成什么样的功能,并放在正确的位置,完成一步积木的搭建。通过多次这样的过程,即可搭建成自己想要完成的东西,即调用不同的函数,拼接、累加完成一段程序。自己动手,丰衣足食。在本章,我们将自己编写函数,做自己想做的事。 【问题来了】 《西游记》中师徒四人在取西经的路上,三位徒弟面临师父被妖怪抓走时,会有不同的举措,怎样做到说出一位徒弟的名字便知道他的举措呢? 回忆《西游记》中孙悟空、猪八戒和沙僧遇到师父被妖怪抓走时,孙悟空会说: “妖怪,快还我师父!”; 猪八戒说: “师父都让妖怪给吃了,你回你的花果山,我回我的高老庄得了!”; 而沙僧会说: “师兄,师父被妖怪抓走了!”。那么,怎样使用Python语言通过函数实现呢? 5.1积木的制作——函数定义 在本章之前,其实大家已经多次接触过函数了,如print()就是一个函数,但print()是一个内置函数。大家常用的一些函数都是内置函数,如求最大值、最小值等。当然也可以私人定制一些函数,实现自己想要的功能,这些函数不是Python自身携带的,但我们可以定制出这样的函数。函数的优点是: 一旦定义了函数,可以反复使用,无须重复做一样的事情使得代码冗长,让人看了就提不起精神。 5.1.1什么是函数 在数学的世界里,给定一个集合A,对A进行操作f,记作f(A)。得到另一个集合B,也就是B=f(A),那么这个关系式就叫作函数关系式,简称函数。 图51函数示意图 在程序的世界里,函数如图51所示,是一个固定的子程序,它在可以实现固定运算功能的同时,还带有一个入口和一个出口。 入口,就是函数所带的各个参数,我们可以通过这个入口,把函数的参数传入子程序,让计算机进行处理; 出口,就是函数的函数值,在计算机操作完成后,将结果返回给调用它的程序进行输出。 在现实生活中,自动面包机就像一个函数,而面粉、酵母、水等原材料作为参数进行输入。原材料经过函数这个面包机就会输出面包这个产物。 这里以max()函数为例帮助理解。当求一个列表的最大值的时候使用的是 max([3, 7, 1, 6, 9]),得到的结果是9。那么 [3, 7, 1, 6, 9] 就是原材料,是函数的入口,而9就是出口处的结果,是函数执行之后的结果。 首先,我们了解一下函数长什么样子。函数由3部分组成: 名称、参数和函数体。函数定义的格式是: def 函数名(参数): 函数体 注意: 函数命名规范: 尽量保持一致,首字母小写,多个字母的函数名使用下画线分隔; 如 add_two_nums(),find_me()等。 如下,def表示开始定义函数,function是给函数起的名字,小括号内便是入口,此例入口处无参数进入,冒号下面是函数体,至此,函数的第一行就结束了。第二行的函数体和正常的Python语句一样,执行名为function函数的功能,此处是将“《西游记》”输出。第三行是对函数的调用,具体函数调用的过程,将在后面为大家讲解。 def function(): print('《西游记》') function() 注意: 函数体中的print前面有一段空格,函数体的内容要比正常的语句的左边多四个空格符!这就是函数体需要注意的格式问题。 下面我们看一下函数名后面的括号内有无内容(参数)的情况。 【练一练】 用函数的形式打印出“Hello”。 5.1.2空函数 pass是Python语言提供的一个关键字,执行该语句的时候什么都不做,是一条空语句。在设计模块时,对于一些细节问题或功能在以后需要时再加上的情况下,可以在准备扩充的地方先写上空函数,这样使得程序结构清晰,可读性高,且易于扩充。 如下所示,定义函数名为empty的函数,函数体只有pass,不执行任何操作,这样的函数称为空函数。 def empty(): pass 给定两个数,通过定义compare()函数对这两个数进行比较,若前者比后者大,则不做任何操作,否则进行error提示。 def compare(a,b): if a>b: pass else: print("error") compare(1,2) compare(2,1) 运行结果: error 注意: 结果只返回一个error,第一次调用不满足1比2大的条件,返回error,第二次调用,2比1大,满足函数体内第一个if判断,执行pass,无结果返回。 5.1.3无参函数 对于《西游记》中的师徒四人,想要知道他们不同的举措,首先要将这个团队的名称打印出来,这样才能对应他们的举措。如下,定义一个名为team()的函数,用来输出《西游记》中的成员名称。 def team(): print("唐僧") print("孙悟空") print("猪八戒") print("沙僧") team() 第一行def关键字表明函数定义的开始,定义一个名为team()的函数,括号内为空,表明是无参的函数,即不需要输入。冒号下面的4行是整个函数体。team()语句用来调用team()函数,调用后输出函数内定义好的“唐僧”“孙悟空”“猪八戒”“沙僧”四个字符串。 观察函数体内的定义,用了四个print()函数打印出四行字,这样会显得代码很长且没有技术性,回顾前面的特殊字符“\n”,发现只需要一个print()函数,在“唐僧”“孙悟空”“猪八戒”和“沙僧”的后面加上换行符“\n”,简单的一行代码就可以实现同样的功能,又方便快捷了许多。 def team(): print("唐僧\n孙悟空\n猪八戒\n沙僧\n") team() 运行结果: 唐僧 孙悟空 猪八戒 沙僧 如上,如何实现师父被抓走时,直接全部输出孙悟空、猪八戒和沙僧会说的话呢? def action(): print('孙悟空: "妖怪,快还我师父!"') print('猪八戒: "师父都让妖怪给吃了,你回你的花果山,我回我的高老庄得了!"') print('沙僧: "师兄,师父被妖怪抓走了!"') action() 运行结果: 孙悟空: "妖怪,快还我师父!" 猪八戒: "师父都让妖怪给吃了,你回你的花果山,我回我的高老庄得了!" 沙僧: "师兄,师父被妖怪抓走了!" 如上,函数名为action()的无参函数,三句print()可直接实现师父被抓时,徒弟们的举措。由于print()的内容较长,可分三个print()进行打印,这样显得清楚有序。 至此,我们接触的都是无参函数。函数若无参数,循环也能完成这样的工作,但函数有参数时,一样的函数由于不同的输入会有不同的输出,这可是循环办不到的事情!因此,我们将为大家讲解一下有参数的情况。 【练一练】 在函数体内加一行代码,将“白龙马”加入该团队。 5.1.4有参函数 每次我们调用上述team()函数时,只能固定输出“唐僧”“孙悟空”“猪八戒”“沙僧”的名字,但不是所有的team()都是他们四个人,也可以由我们告诉它团队成员是谁。如下,定义一个名为team()的函数,参数是name,输出的也是name。形参,顾名思义,是一个形式上的参数,当调用函数,实际参数即实参传入函数时,函数体中形参的值都为实参的值。注意,此例中name只是个形参,但实际调用时,输入的“唐僧”作为实参才会被输出。此处的函数可以理解为一个模板,谁用就写上谁的name。 def team(name): print('%s'%name) team("唐僧") 运行结果: 唐僧 但有时,不知道参数要写什么样的,可以给参数传入默认值,无实参传入时,便使用默认值。如下,团队成员默认为“唐僧”,调用team(),无实参输入,输出“唐僧”。 def team(name="唐僧"): print('%s'%name) team() 运行结果: 唐僧 提到唐僧就想到孙悟空,如下,调用team()函数,将“孙悟空”作为实参传入时,默认参数“唐僧”并不影响函数调用。 def team(name="唐僧"): print('%s'%name) team("孙悟空") 运行结果: 孙悟空 《西游记》中有一个经典桥段,孙悟空让唐僧待在自己画的保护圈内,避免妖怪袭击。那么圆圈的大小怎么能够改变呢?用函数试一试吧!在数学里,一个简单的圆的面积公式πr2就能解决这个问题,只需要知道半径r就可以根据面积公式计算得出面积。如下,定义一个circle()函数,半径r作为参数,由于r不同,每次调用函数的结果就不同。 import math def circle(r): area=math.pi*r*r return area print(circle(2)) 运行结果: 12.566370614359172 注意: 此处函数体内没有用print()函数,而是用了return关键字。在函数调用时却使用了print()函数进行输出,有什么区别呢? 我们将return修改为print,记住print()函数要加括号。运行,发现多了一行None,这表明没有返回值,只需要调用,无须再用print()。 import math def circle(r): circle=math.pi*r*r print(circle) print(circle(2)) 运行结果: 12.566370614359172 None 用return的好处就是结果不仅可以输出,显示在屏幕上,也可以进行加减等运算操作。如下调用时,将两次调用结果相加会得到两个圆的面积。 import math def circle(r): circle=math.pi*r*r return circle print(circle(2)+circle(2)) 运行结果: 25.132741228718345 此处半径r可以是整型,也可以是浮点型,但不接受字符型或字符串。 import math def circle(r): circle=math.pi*r*r print(circle) a='2' print(circle(a)) 运行结果: Traceback (most recent call last): File "文件路径", line 6, in print(circle(a)) File "文件路径", line 3, in circle circle=math.pi*r*r TypeError: can't multiply sequence by non-int of type 'float' 此处的函数只有一个形参,那两个形参呢? def summation(a,b): return a+b print (summation(1,2)) 三个、四个、五个呢?我们可以使用元组或列表表示。 def summation(A): s=0 for i in A: s=s+i return s A=([1,2,3,4,5]) print (summation(A)) 如上,A是一个列表,函数体内通过for循环语句对列表进行遍历求和。 求和可直接使用Python的内置函数sum。 A=([1,2,3,4,5]) print(sum(A)) 以上接触的函数都只有一个返回值,如果有多个返回值该怎么办呢? 首先看一下有两个返回值的情况。 例如,函数y=x2已知y值,求x的值。y=0时,x=0; y>0时,x会有两个返回值。 import math def f(y): if y==0: x1=0 x2=0 return x1, x2 elif y>0: x1=math.sqrt(y) x2 = -math.sqrt(y) return x1, x2 print(f(4)) 运行结果: (2.0, -2.0) 代码的第一行导入math模块是为了引用math.sqrt()函数,此函数对括号内的数值进行开根号处理。函数体通过两个if语句进行判断,决定输出结果是一个还是两个,两个返回结果之间可用逗号隔开。这是在知道输入值必须是非负数的情况下,如果一不小心,输入有误,输入了负数,会返回什么结果呢?程序当然就会报错。为了避免这样的错误,我们可以给代码再加一个if判定条件。如下所示,如果输入值为负数,返回“输入有误,请输入一个大于或等于0的数!”的字符串进行提醒。 import math def f(y): if y==0: x1=0 x2=0 return x1, x2 elif y>0: x1=math.sqrt(y) x2 = -math.sqrt(y) return x1, x2 elif y<0: return ("输入有误,请输入一个大于或等于0的数!") print(f(-4)) 【练一练】 根据圆的周长公式,定义一个圆的周长函数。 5.2动手搭积木——函数调用 函数调用后,大家才能看见结果,否则程序无输出,正如一盘散沙的积木,不搭建,是不会成型的。 函数定义成功后,进行函数调用,确认函数正确后,在下次使用此函数时,无须关心函数内有什么,只需知道这个函数需要输入什么,可以实现的内容即可。那么前面一直提到的函数调用是什么呢? 下面为大家讲解一下函数调用的过程,以画圆为例。 下列程序的第一行是引入math模块,使用math.pi语句来调用模块内的圆周率。第二行到第四行是circle()函数体。第二行,def定义参数为r的circle()函数。第三行计算面积,并将结果赋值给变量area。第四行返回的是area的值。第五行调用circle()函数,并输出r为2时的结果。 程序执行的过程是: 首先,函数定义是不执行的,程序从print(circle(2))开始,调用函数,带着参数2跳回第二行,第二行是定义,不执行。接着,依次执行函数体的每一行代码,至函数完成,离开函数,回到调用函数的位置继续往下执行。 import math def circle(r): area=math.pi*r*r return area print(circle(2)) 向函数传递参数,形参相当于一个指代词。在调用时会明确它是什么的参数,称为实参。 学习了简单的函数的定义及调用,回顾一下本章一开始的师父被抓的问题,代码可以这么写: def action(name): if name=="孙悟空": print('孙悟空: "妖怪,快还我师父!"') if name=="猪八戒": print('猪八戒: "师父都让妖怪给吃了,你回你的花果山,我回我的高老庄得了!"') if name == "沙僧": print('沙僧: "师兄,师父被妖怪抓走了!"') action("孙悟空") action("猪八戒") action("沙僧") 运行结果: 孙悟空: "妖怪,快还我师父!" 猪八戒: "师父都让妖怪给吃了,你回你的花果山,我回我的高老庄得了!" 沙僧: "师兄,师父被妖怪抓走了!" 不用着急,让我们一起来分析代码。第一行def关键字表明函数定义的开始,def紧接着的便是定义名为“action”的函数,括号用来传递参数,形参名为name,表明此函数在调用时需输入一个实参。形参就相当于指代词“他”,而实参就是将指代词具体到一个人或一件事。在函数体中,正常编写实现功能的函数即可,此处函数体完成了三次判断和输出功能。再次注意函数体的格式区别于正常书写,须整体右移四个空格。函数定义完成后,使用函数名和实参进行调用。 def action(A): for name in A: if name=="孙悟空": print('孙悟空: "妖怪,快还我师父!"') if name=="猪八戒": print('猪八戒: "师父都让妖怪给吃了,你回你的花果山,我回我的高老庄得了!"') if name == "沙僧": print('沙僧: "师兄,师父被妖怪抓走了!"') A_list=['孙悟空','猪八戒','沙僧'] action(A_list) 定义名为action()的函数,A为形参。for循环对A中元素进行遍历,每个遍历元素为name,如果name等于字符串“孙悟空”,则输出字符串——孙悟空: “妖怪,快还我师父!”,以此类推,实现师父被抓走时,徒弟们表现出的不同举措。 结合前面几章,我们结合第2章的数据类型、第3章的循环和判断以及第4章的列表实现师父被抓走时,徒弟们表现出的不同举措。 def action(A): for name in A: if name=="孙悟空": print('%s'%name+': "妖怪,快还我师父!"') if name=="猪八戒": print('%s'%name+': "师父都让妖怪给吃了,你回你的花果山,我回我的高老庄得了!"') if name == "沙僧": print('%s' % name + ': "师兄,师父被妖怪抓走了!"') A_list=['孙悟空','猪八戒','沙僧'] action(A_list) 定义名为action()的函数,A为形参。通过第3章所学的for循环语句对第4章学的列表A中的元素进行遍历,同样用第3章所学的if语句进行判断。以第2章学的字符串类型%s,用变量name的值作为主语进行输出。 【知识拓展】在函数内有变量,在函数外也有变量,那么它们两个有区别吗?即使变量名一样也是有区别的。函数内的变量称为局部变量,函数外的变量称为全局变量。 为了准确区分局部变量和全局变量,在上面师父被抓走的案例中,尝试加最后一行代码,将变量name输出试试。 def action(A): for name in A: if name=="孙悟空": print('%s'%name+': "妖怪,快还我师父!"') if name=="猪八戒": print('%s'%name+': "师父都让妖怪给吃了,你回你的花果山,我回我的高老庄得了!"') if name == "沙僧": print('%s' % name + ': "师兄,师父被妖怪抓走了!"') A_list=['孙悟空','猪八戒','沙僧'] action(A_list) print(name) 运行结果: Traceback (most recent call last): File "文件路径", line 11, in print(name) NameError: name 'name' is not defined 程序报错,显示name未定义,但明明程序中有name,这是为什么呢?是因为变量name在函数内,它的作用范围只在函数内,在函数外是不存在的,所以程序会报错。那么再尝试定义一个全局变量name,如下所示。 def action(A): for name in A: if name=="孙悟空": print('%s'%name+': "妖怪,快还我师父!"') if name=="猪八戒": print('%s'%name+': "师父都让妖怪给吃了,你回你的花果山,我回我的高老庄得了!"') if name == "沙僧": print('%s' % name + ': "师兄,师父被妖怪抓走了!"') A_list=['孙悟空','猪八戒','沙僧'] action(A_list) name='唐僧' print(name) 运行结果: 孙悟空: "妖怪,快还我师父!" 猪八戒: "师父都让妖怪给吃了,你回你的花果山,我回我的高老庄得了!" 沙僧: "师兄,师父被妖怪抓走了!" 唐僧 在此例中,变量name多次被使用,当打印变量name值时输出的是“唐僧”,并没有“孙悟空”“猪八戒”“沙僧”出现,这是为什么呢? 我们分析一下代码,首先定义一个名为action()的函数,函数的形参是A,接着在函数体里进行遍历判断,action()函数的分析同上,我们从倒数第三行开始分析,action(A_list)将A_list作为实参调用action()函数,不着急,我们一步步分析调用的过程,看看变量name的变化。调用后,执行for循环语句,第一次变量name是实参A_list中的第一个元素“孙悟空”,执行下一句if判断,满足条件name=="孙悟空",将当前的变量name进行输出,再次进行判断是否满足name=="猪八戒",不满足该条件,程序往下执行,也不满足name=="沙僧"的条件,因为此时的变量name就是“孙悟空”。三次判断结束,程序回到for循环,此时将实参A_list中的第二个元素“猪八戒”赋值给变量name,同上执行三次if判断。三次判断结束后,程序回到for循环,此时将实参A_list中的第三个元素“沙僧”赋值给变量name,同上执行三次if判断。此时实参A_list列表内容结束,函数调用结束。至此,变量name都是局部变量。接着执行倒数第二行程序,将“唐僧”赋值给变量name,程序继续,执行最后一行代码,打印变量name。结果输出了“唐僧”,此处的name变量是一个全局变量。代码最后一行中的print()出来的不会是局部变量,在函数调用时,函数外的变量不会被使用,在函数调用结束后,函数内的变量也不复存在了。 注意: 在此建议大家变量名尽量不要重复! 以上案例的文字显得有点儿单调,下面我们尝试用图的形式进行表示,显得更为直观。以学生的成绩统计为例,成绩如表51所示,以直方图的形式可以很直观地看出学生成绩的分布。 表51四年级8班学生数学成绩统计表 姓名学号成绩 刘一0180 王二0298 张三0369 李四0490 王五0578 赵六0683 孙七0789 周八0855 吴九0985 郑十1088 从表51中可以看出每位学生的成绩,但并不直观,如何用图的形式表示出来呢?我们需要借助matplotlib库里的pyplot进行画图,通过import…as…将其简化。定义直方图,有五个参数: 横坐标、纵坐标、图例名称、横坐标名称、纵坐标名称。绘制出的直方图如图52所示。 import matplotlib.pyplot as plt from pylab import mpl mpl.rcParams['font.sans-serif']=['SimHei'] def bar_chart(x_list,y_list,label_name,x_name,y_name,color): plt.bar(x_list,y_list,label=label_name,facecolor= color) plt.xlabel(x_name) plt.ylabel(y_name) plt.show() x=['刘一','王二','张三','李四','王五','赵六','孙七','周八','吴九','郑十'] y= [80,98,69,90,78,83,89,55,85,88] bar_chart(x,y,'math','Name','Grade') 图52四年级8班学生数学成绩直方图 注意: 此处的matplotlib包需要自己安装,如果程序报错,请查看是否有安装相应的包,如何安装包,见附录B。从pylab库中导入mpl,设置中文字体mpl.rcParams['font.sansserif']=['SimHei'],SimHei为简黑字体。 【练一练】 (1) 将例题中学生的成绩用折线图表示出来。 (2) 将例题的数据以表格的形式存储学生成绩,使用文件操作将数据以条形图表示出来。 5.2.1函数的嵌套调用 思考一下,在一个函数内,是否可以调用其他函数呢?答案是可以的。并且它有一个名字叫作函数的嵌套调用。 如图53所示,按照圆圈中数字的顺序,调用函数f1,函数f1执行时,调用函数f2,执行函数f2得到一个返回值返回到调用函数f2处,调用函数f2,得到的返回值返回到调用函数f1处。 图53函数嵌套调用示意图 思考一下,一杯糖水,糖与水的比例是1∶m,喝掉一半后,又用水加满,如此稀释n次后,糖与水的比例是多少呢?此题的关键是糖到底有多少?变量original_r存放的是原来糖占糖水的比例。喝掉一半后,还剩的糖占比放在变量half_r里。倒满水后,糖与水的占比放在变量full_r里,并将full_r值进行返回,完成一次糖稀释的过程。 def syrup(m): original_r=1 / (1 + m) #原来糖占的比例 half_r= original_r/2 #喝去一半后还有糖 full_r=half_r/(1 - half_r)#倒满后糖与水的比例 return full_r def times(m,n): for i in range(1,n+1): m=syrup(m) return m#i次后糖的占比 print(times(5,1)) 运行结果: 0.09090909090909091 此处有两个def,即有两个函数,一个名为syrup(),另一个名为times()。从上往下看,第一个函数syrup()是计算1∶m的糖和水稀释一次后糖和水的比例。第二个函数times()是稀释n次后糖和水的比例。在times()函数里调用syrup()函数,产生的值重新赋值给变量m。这个过程便是函数嵌套调用的过程。times()函数内通过for循环语句,将m再次作为syrup()函数的参数进行调用,达到n次稀释的作用。 注意: 此处m只接受正实数,否则程序便会报错。 5.2.2函数的递归调用 函数的递归调用是一种特殊的嵌套调用。 如5!=5×4!=5×4×3!=5×4×3×2!=5×4×3×2×1!,1的阶乘是1,且规定0的阶乘是1,然后从后往前推算。 结合以前的数学知识,可知阶乘公式可表示为: n!=1,n=0,1 n×(n-1)!,n为大于1的正整数 像这样函数调用自己本身称为函数的递归调用。 def f(n): if n==0: n_j=1 return n_j elif n>0: n=n*f(n-1) n_j = n return n_j else: return '请输入一个非负数!' print(f(10)) 运行结果: 3628800 如下所示,定义函数名为f的函数,函数体第一行判断输入的数值是否为0,如果是0,变量n_j赋值为1,因为0的阶乘是1,当n>0时,通过阶乘公式,调用自己本身函数f()进行计算。n从大到小进行递归调用。 此处阶乘函数明显不严谨,若输入的是个浮点型的数值呢? def f(n): if n==0 and type(n)==int: n_j=1 return n_j elif n>0 and type(n)==int: n=n*f(n-1) n_j = n return n_j else: return '请输入一个非负整数!' print(f(1.1)) 运行结果: 请输入一个非负整数! 在if判定条件处分别再加上类型判别,若是整型才可以进行阶乘,否则转到else的情况,进行输入提示。 大家想一想,数值、字符串都可以进行加操作,函数可以吗?答案是可以的。请看: def f(n): if n==0 and type(n)==int: n_j=1 return n_j elif n>0 and type(n)==int: n=n*f(n-1) n_j = n return n_j else: return '请输入一个非负整数!' def s_f(n): sum=0 for i in range(1,n+1): sum=sum+f(i) return sum print(s_f(5)) 运行结果: 153 计算公式s_f(n)=1!+2!+…+n!,代码块中第一个函数如上一个案例,是一个阶乘函数,第二个s_f(n)是对f()函数进行求和的函数。在s_f()函数内,首先变量sum表示和,初始化sum为0,通过for循环语句,将1~n的阶乘进行相加,最后返回sum。 5.3猜数字游戏(案例) 游戏规则: 选手背对大屏幕,大屏幕会随机地出现范围内的任一数字,作为“数字炸弹”。选手根据主持人给的数字范围依次随机地说出一个数字,主持人根据选手说出的数字,逐渐缩小数字范围,直至有位选手说出“数字炸弹”,游戏结束,则该选手接受惩罚。注意: 选手说出的数字,不含范围边界上的数字。 import random def number_bomb_game(start,end): boom_number=random.randint(start+1,end-1)#保证随机炸弹不是边界数字 print(boom_number) while True: print('请输入一个%d到%d范围内的数:'%(start,end)) number = input() if number.isdigit():#判断输入的是否为数字 if int(number) > start and int(number) < end : break else: print("输入有误!") else: print("输入有误!") while int(number)!=boom_number: if int(number) int(start) and int(number) < int(end): break else: print("输入有误!") else: print("输入有误!") else: end = number while True: print('请输入一个%d到%d范围内的数:' % (int(start), int(end))) number = input() if number.isdigit(): # 判断输入的是否为数字 if int(number) > int(start) and int(number) < int(end): break else: print("输入有误!") else: print("输入有误!") else: print('game over') number_bomb_game(1,10) 运行结果: 3 请输入一个1到10范围内的数: 8 请输入一个1到8范围内的数: 4 请输入一个1到4范围内的数: 3 game over 如上,假定代码运行的第一行产生的随机数只有主持人知道,题目确定数字炸弹是一个1~10的数,例如3,选手1输入8,3在1~8的范围内,主持人给出“请输入一个1到8范围内的数”的提示,选手2输入了4,3在1~4的范围内,主持人给出“请输入一个1到4范围内的数”的提示,选手3输入了3,主持人提示“game over”,游戏结束,数字炸弹就是3,选手3接受惩罚,游戏结束。 5.43的倍数小游戏(案例) 游戏规则: 人数不限,依次排好序,从1开始报数,若自己的数是3的倍数,则不出声(空格代替),否则说出(输入)自己的数字,若选手没有按游戏规则进行,则淘汰选手,最后仅剩的一个人胜利。这次,大家借助代码后面的注释进行理解。 # 函数名为three_times,形参为number,用来传入游戏参加的人数 def three_times(number): member=[] # member列表存放游戏成员 for i in range(1,number+1): member.append(i) # 对成员从1开始编号 # 提示输入 print('-'*50) print("【游戏开始】共{}名游戏成员,请从1开始报数:".format(number)) print('【提示】结束比赛请输入【#】,个人退赛请输入【*】') print('-'*50) count = 0 # 记录轮次数 cur = 0 # 记录当前轮次的成员的位置 # 游戏不停地淘汰成员,直到只剩一名成员 while len(member) != 1: count += 1 # 判断当前位置的成员是否超过列表的范围 if cur >= len(member): cur = 0 exp = 0 # 记录成员应该输入的值 # 计算预期输入 if count % 3 == 0: exp = ' ' else: exp = str(count) # 输入的值赋给input_number变量 input_number = input('{}号请输入:'.format(member[cur])) # 如果成员选择结束比赛 if input_number == "#": print('游戏中途结束') return # 如果成员选择退出比赛 if input_number == "*": print('【退出】{}号成员退出比赛,其他人继续'.format(member[cur])) member.pop(cur) # 从成员列表中删除 continue # 如果输入的数字不符合规则 if input_number != exp: print('【淘汰】{}号成员淘汰'.format(member[cur])) member.pop(cur) # 从成员列表中删除 continue cur += 1 # 游戏直到只剩1人,游戏结束,该成员胜利 print("\n【游戏结束】{}号获胜\n".format(member[0])) three_times(5) 运行结果: -------------------------------------------------- 【游戏开始】共5名游戏成员,请从1开始报数: 【提示】结束比赛请输入【#】,个人退赛请输入【*】 -------------------------------------------------- 1号请输入:1 2号请输入:2 3号请输入:3 【淘汰】3号成员淘汰 4号请输入:4 5号请输入:5 1号请输入: 2号请输入:6 【淘汰】2号成员淘汰 4号请输入:7 【淘汰】4号成员淘汰 5号请输入:9 【淘汰】5号成员淘汰 【游戏结束】1号获胜 说明: 例如,游戏设定5人,1号成员输入1,2号成员输入2,3号成员输入3,3是3的倍数,应该输入空格,3号成员淘汰;4号成员输入4,5号成员输入5,1号成员输入空格,均正确;2号成员应该输入7,却输入了6,2号成员淘汰;4号成员应该输入8,却输入了7,4号成员淘汰;5号成员应该输入空格,却输入了9,9是3的倍数,5号成员淘汰;游戏仅剩一人,本案例中1号成员获胜! 【过关斩将】 (1) 用函数打印一张九九乘法表。 (2) 输入圆柱体底面的半径r和柱高h,输出圆柱体的体积。(小提示: 圆柱体体积=底面周长×高。) (3) 绘制你们班的成绩直方图。 (4) 在直方图的案例里,虽然图的形式很直观,但看到的数据不是特别准确,你是否可以在条形图的上面加上数值呢? 小结 本章学习了函数的使用,在前面学习的基础上,将具有一定功能的代码块用函数表示,后期使用时直接调用,既方便又快捷,代码也十分工整。主要有三种函数: 空函数、无参函数和有参函数,其中,有参函数较灵活,使用居多。学习了本章,请你在下面的知识点中,在已经学会的知识前打勾。 □无参函数 □有参函数 □函数定义 □函数调用 □函数嵌套 □函数递归 □全局变量 □局部变量