当前位置:首页>python>自学编程第20课:Python综合练习——用函数把猜数字游戏“拆”成积木

自学编程第20课:Python综合练习——用函数把猜数字游戏“拆”成积木

  • 2026-10-11 07:42:51
自学编程第20课:Python综合练习——用函数把猜数字游戏“拆”成积木

Tyree前几天把他那个猜数字游戏邀同学一来一起玩。同学玩了一局,说:“每次结束都要重新运行程序,太麻烦了。能不能玩完一局自动问‘再来一局’?” Tyree一拍大腿:“对啊,我怎么没想到!”

于是他在程序后面加了while True和input("再玩一局?"),解决了这个问题。但他看着代码有点不舒服:整个游戏挤在一起,上百行,想改一个地方要翻半天。

他跑来跟我说:“妈妈,你看我这代码,乱七八糟的。能不能把游戏拆成几块?比如‘输入数字’是一块,‘判断大小’是一块,‘记录成绩’是一块……这样我以后想改‘猜数字范围’或者‘限制次数’,只改那一块就行了。”

我说:“这不就是我们之前学的函数吗?你把每个独立功能写成一个函数,主程序只负责调用它们。以后你想加‘难度选择’或者‘历史最佳成绩’,直接加函数,不乱动别的。”

Tyree听完,花了一个晚上,把游戏拆成了四个函数:

get_valid_input(专门管输入)、check_guess(专门管比较)、play_one_round(管一局流程)、main(管整体)。

改完后他兴奋地说:“现在我的代码像乐高积木一样,想拆就拆,想拼就拼!”

今天我们就跟着Tyree的思路,把猜数字游戏彻底用函数重构一遍。

01

先写“管输入的”:get_valid_input

这个函数只管一件事:死皮赖脸地问你要一个合格的数字,你要是不给数字,或者给的范围不对,它就跟你没完。

这个函数,名字叫 

get_valid_input,翻译过来就是“拿到一个有效的输入”。

它干的事就只有一个:死皮赖脸地问你要数字,你要是不给数字,或者给的范围不对,它就跟你没完。

里面那个 while True 是个死循环,意思就是“你不给我对的,我就不走了”。

Tyree第一次看到这个写法,说:“这不是耍赖吗?”我说:“对,编程里这叫‘防御式编程’,就是防着用户瞎输入。”

第一关:isdigit()。用户有可能输入“abc”或者“一二三”,isdigit() 就返回 False,然后打印“输入的不是数字”,接着 continue 跳回去重新问。

第二关:检查范围。比如你要求1到100,他输入200,程序就说“数字超出范围”,然后 continue 回去重新问。

最后,啥都合格了,

return guess。这一句一执行,函数就结束了,那个死循环终于被打破。

“为什么不用 break?”

“return 不仅跳出循环,还顺便把值带回去。break 只是跳出循环,你还得再写一行 return,多麻烦。″

02

再写“管判断的”:check_guess

这个函数只干一件事:把你猜的数字跟秘密数字比一下,然后告诉你结果。

就这么几行,一点都不复杂。但Tyree问了一个好问题:“为什么不用 print 直接打印,非得用 return 把结果送回去?”

我给他打了个比方:print 就像你在屋里大喊一声“猜中了”,外面的人听见了,但也就听个响。

return 就像你走到那人面前,把一张纸条递给他,纸条上写着“猜中了”。他拿到纸条,可以念出来,也可以存起来,甚至转交给别人。是不是更灵活?

后来Tyree想给猜数字游戏加一个功能:猜错的时候等1秒再提示,制造紧张感。

如果用的是 print,他就得在 check_guess 函数里面改;如果用的是 return,他只需要在外面调用 check_guess 的地方,拿到结果之后,再决定什么时候打印。

03

写“管一局游戏的”:play_one_round

这个函数负责一局的完整流程:生成一个秘密数字、计时、反复问用户猜、直到猜中或者次数用完,最后返回这一局用了多少次、多少秒。

这个函数稍微长一点,但拆开看就清楚了。

第一行 

secret = random.randint(...) 生成一个随机数,这是游戏的核心。

start = time.time() 掐表开始。

while attempts < max_attempts: 循环,只要没到次数上限就一直猜。

循环里面调用了之前写的两个函数:get_valid_input 拿数字,check_guess 比较结果。

如果猜中了,break 跳出循环,后面那句“机会用完了”就不会执行。

如果次数用完了还没猜中,就打印正确答案。

最后 end = time.time() 掐表结束,算出用时 elapsed。

函数把 attempts(猜的次数)和 elapsed(用的秒数)一起返回。

Tyree测试这个函数的时候,有一次运气爆棚,第一次就猜中了,只用了1次和2.3秒。

他兴奋地拿笔记下来:“最高纪录:1次猜中!”

后来他还故意把 max_attempts 改成1,想看看能不能每次都一次猜中,结果发现概率只有百分之一,他说:“果然不是我能控制的。”

注意这里几个参数 

min_num=1,max_num=100, max_attempts=5 都给了默认值。

这样以后你想玩不同范围或者不同次数,直接传新值就行,不改原来的代码。Tyree说:“默认参数就像‘懒人模式’,大部分时候直接用,特殊时候再自己调。”

04

最后写“管全局的”:main

这个函数负责整个程序怎么跑:启动游戏、问你要不要再来一局、记录历史最佳成绩。

best_attempts = float('inf') 这行看起来有点怪。float('inf') 是正无穷大,意思就是“现在还没有成绩,谁都比这个好”。

第一次玩完之后,成绩肯定比无穷大小,所以就会被记下来。

Tyree刚开始写的时候用 best_attempts = 0,结果后面比较 attempts < 0 永远不成立,最佳成绩一直是0。

他挠头说:“怎么不更新?”我告诉他:“0比任何正数都小,你第一次猜了5次,5不小于0,当然不更新。”他改成无穷大之后就好了。

round_num 就是个计数器,显示第几局,没什么实际作用,但看着舒服。

again = input(...).strip().lower() 这一行处理用户的输入。

.strip() 去掉首尾空格,.lower() 转成小写。这样用户输入“Y ”、“y”、“YES”都能正确识别为“y”。

Tyree一开始只判断 again == 'y',结果同学输入“Y”就不行,他加了这两招之后就通吃了。

最后,如果用户输入不是 y,break 跳出循环,游戏结束。

05

拼起来:完整代码

把上面所有函数放在一个文件里,最上面导入模块,最后加上 if __name__ == "__main__":。

07

今天我们学了什么?

拆分函数一个函数只干一件事,好改、好测、好复用。

return把结果递出来,外面想怎么用就怎么用默认参数调用时可以偷懒不传,省事。

main()把程序的主逻辑放一起,一看就懂。

if __name__ == "__main__"防止被别人当模块导入时自动运行。

好了,今天课程就讲到这。

下一课开始,我们要玩点有意思的了:文件读写、字符串进阶,还要做三体名言抽卡、水熊虫生存模拟。Tyree已经等不及了。

————热门推荐————

自学编程第19课:Python time模块——让程序学会“等待”和“计时”

自学编程第16课:Python函数的偷懒技巧——默认参数、关键字参数、可变参数

自学编程第7课:turtle画图入门(画一个正方形,五角形,螺旋形,三角形)

自学编程第6课:列表+循环+判断(做一个学生成绩分析器)

自学编程第3课:变量与数学计算(做一个年龄计算器)

自学编程第2课:用input让电脑问你名字(做一个打招呼程序)

自学编程第一步:安装Python和Thonny(零基础图文教程)

(本系列教程每天更新,欢迎关注收藏)

最新文章

随机文章