
1. 给“别人”写的程序到底难在哪上周我带的一个五年级孩子跑来找我说他把猜数字游戏发给了同桌玩同桌乱输了一通程序直接报错闪退两人在教室里面面相觑。我说这太正常了你以前写的程序基本都只有自己一个人用自己知道哪里能按、哪里不能按程序自然怎么跑都顺可一旦程序要给别人用你就要面对一个完全不按剧本走的“真实用户”。这一课想解决的就是这个从“自用”到“他用”的思维转变。很多孩子学Python学到第29课语法已经学了蛮多变量、判断、循环、函数甚至能写出挺像样的小游戏。但大家普遍缺一个东西——不是新的语法而是“给别人用的程序要注意什么”。注意输入合不合法、注意程序扛不扛得住乱操作、注意提示语能不能让一个陌生用户看懂这些才是写完代码之后最该过的一道关。我经常在课上打一个比方自己做饭给自己吃炒咸了炒糊了都能凑合反正你自己知道这道菜是什么味但要开一家店把菜端给陌生人吃你就要考虑别人吃不吃得惯、菜里有没有骨头、招牌上有没有写清楚价格。程序也是一样写给自己跑逻辑对就行写给别人用得让别人用得明白、用得不慌张、用的时候不会把程序“玩坏”。这一课的内容适合两类人看。一方面是正在学Python的孩子尤其是已经会写一些简单小程序、想把自己做的东西分享给同学朋友的那一批另一方面是带孩子学编程的家长和老师很多成人自己写脚本习惯了教孩子时反而容易忽略这层“用户视角”的引导。接下来我会用一个课堂上的真实例子把“给别人用”这件事从头到尾拆透。2. 一次课堂翻车实录成绩判断器的四版进化2.1 第一版键盘一乱就崩溃上课的时候我让孩子们写一个“成绩判断器”输入一个成绩输出优秀、良好、中等、及格或不及格。大部分孩子第一版写出来长这样score int(input(请输入成绩)) if score 90: print(优秀) elif score 80: print(良好) elif score 70: print(中等) elif score 60: print(及格) else: print(不及格)这个代码自己用一点问题都没有。但我让同桌之间交换着测试现场马上开始出状况。有人在输入框里敲了“88分”有人输了“abc”还有人直接按了回车程序全部当场崩溃报出一长串红色英文。孩子们一脸懵刚才在自己电脑上好好的怎么换个人用就坏了问题出在哪出在代码里有一个假设——程序默认用户一定会输入一个像“85”这样的纯数字。可实际使用的人不会按你的剧本来他可能输入带单位的文字可能不小心碰到字母键可能手滑按了回车。在编程里这类输入叫“非法输入”而你的程序根本没有对非法输入做任何处理。我给孩子们看了一张“用户操作图”你坐在电脑前写代码脑子里想的是一个理想用户他永远输入正确的内容永远按你设计好的顺序操作。但真实用户是另一个物种他可能输入中文、输入负号、输入小数点、输入超大的数甚至还没看清楚提示就按了回车。所以给别人用的程序第一关就是“乱输入也崩不了”。2.2 第二版防出错了但体验还是差发现问题之后孩子们反应很快立刻说“用try语句”。他们学的异常处理派上了用场第二版变成了这样try: score int(input(请输入成绩)) if score 90: print(优秀) elif score 80: print(良好) elif score 70: print(中等) elif score 60: print(及格) else: print(不及格) except: print(输入有误)这版确实不会再崩溃了但我让同桌继续测试新的问题又冒出来了用户一旦输错程序只剩一句“输入有误”然后就结束了。一个同学输错了想重新输入就不得不把程序从头再运行一遍。多试几次之后同桌直接吐槽“你这程序太不友好了错了连个重来的机会都没有。”这里要补充一个细节第二版里那个光秃秃的except也不推荐写。它会把所有错误都吞掉包括用户按CtrlC想中断程序这种操作。正确的写法是精确捕获可能出错的异常类型也就是except ValueError因为你真正要处理的错误就是把字符串转成整数失败这一种。这算是少儿阶段能接触到的第一个“异常处理该收着写”的教训。而且第二版的提示语“输入有误”信息量太少。用户输错了他需要知道错在哪了应该怎么输入才对一个好的错误提示至少要告诉对方“你要输入的是0到100之间的整数”。2.3 第三版让用户可以重新输入到第三版孩子们开始用while循环把输入过程包起来输错了不退出而是回到起点重新问。这已经是很大的进步。while True: score_str input(请输入成绩) try: score int(score_str) except ValueError: print(要输入数字哦不能是字母或文字) continue if score 0 or score 100: print(成绩范围是0到100你输入的数字超出范围了) continue break这里我给孩子们解释了一个关键点input()返回的一定是字符串所以先用一个变量score_str接住再尝试把它转成int。转换失败意味着用户输入的根本不是数字转换成功还要顺手做一次范围校验——你考了1000分这在现实世界里是不存在的。这一段代码最大的思维转变是“数据进门前先安检”。你不是让非法输入一路闯进程序的深处而是在最前面就拦住它。这就像校门口保安谁进来都要先查证件不合格的连校门都不让进。以后大家写任何程序只要遇到用户输入第一反应都应该是这个输入合法吗如果用户乱输入会怎样把这道安检门立在最前面程序就稳了一大半。2.4 第四版完整可交出去的样子第三版解决了“崩溃”和“重试”的问题但离“给别人用”还差最后一口气成绩判断器每次只能输入一个成绩判断完就结束。真实使用场景里老师或者班干部可能要给好几个人连续判断成绩总不能每判断一个就重跑一次程序吧所以第四版我们加了一个完整的循环框架让它能连续使用并且给用户一个明确的退出方式def read_score(): while True: s input(请输入成绩0-100按 q 退出) if s.lower() q: return None try: n int(s) except ValueError: print(请输入数字不要输入文字或字母) continue if 0 n 100: return n print(成绩要在0到100之间请重新输入) while True: score read_score() if score is None: print(已退出再见) break if score 90: print(优秀) elif score 80: print(良好) elif score 70: print(中等) elif score 60: print(及格) else: print(不及格) print(------------------)这版的细节值得一个一个说。s.lower() q能同时处理用户输入大写Q和小写q的情况这一点孩子很容易漏但真实用户敲大写是常有的事。read_score()函数把“读取一个合法分数”这件事单独封装起来主程序里只需要不断调用它代码读起来像自然语言一样顺。退出时给一句明确的“已退出再见”而不是程序悄悄结束用户才能确认自己是正常退出而不是程序出问题了。从第一版到第四版代码量从8行涨到了30行左右但每一行都是“给别人用”逼出来的。我说句实话很多大人写程序也未必一步到位都是这样一版一版被用户教育出来的。孩子们能在一节课里走完这个过程比单纯学十个新语法都有价值。3. 给别人用的程序过这六关才算合格3.1 输入关非法输入不能崩我把四版迭代里学到的东西总结成六关第一关就是“输入关”。这一关要检查的不是“正常输入能不能正确运行”而是“非法输入会不会弄垮程序”。非法输入常见的有这么几类字母和文字、空输入、负数、超范围数字、小数、带了单位或空格的数字、超大数字。很多人以为校验输入就是try...except包一下其实不够。完整的输入校验应该是三道工序第一道类型校验比如成绩必须是整数用int()转换并捕获异常第二道范围校验比如成绩必须在0到100之间第三道格式校验比如用户输入“85分”这种带单位的可以先strip()去掉空格再判断末尾有没有“分”字并处理掉。如果只做第一道不做第二道程序不会崩但会产生“合理但不现实”的结果。比如允许输入500分然后程序高高兴兴地给你判断个“优秀”——这明明就是错的。给别人用的程序不仅要防止崩溃还要防止“静默出错”也就是程序没报错但结果根本不可信。后者比崩溃更麻烦因为用户根本不知道结果有问题。3.2 反馈关每一步都要让用户看得懂第二关是反馈关。一个程序给陌生人用最怕的就是用户不知道当前是什么状态。他打开程序屏幕上什么都没有他输入完程序半天不吭声他输错了程序只回一句“Error”。这些都是反馈没做到位。我在课上要求学生遵守一条规则程序的每一次行为都要让用户知道发生了什么。程序启动时要有欢迎语告诉用户这程序是干吗的等待输入时提示语要写清楚该输入什么、有什么格式要求接收输入后要把用户输入的内容复述出来确认一遍给出结果时要用完整句子而不是一个光秃秃的数字程序结束前要说一句明确的再见。这条规则听起来啰嗦但真实用户体验全靠它。举个例子同样是判断成绩输出“你的成绩是85分等级良好”就比只输出“良好”强得多。前者让用户确信程序没跑错后者会让用户心里犯嘀咕这“良好”是刚才谁的结果所以反馈不是客气话是程序可靠性的重要组成部分。3.3 出口关能退出、能重来第三关最容易在少儿编程里被忽略出口关。我见过太多孩子写的程序进来就出不去要么是死循环要么是运行完自动关闭用户根本来不及看输出结果。给别人用的程序必须想清楚“用户怎么退出”和“用户怎么重新开始”。怎么设计出口最朴素的做法是在程序末尾加一句input(按回车键退出)让窗口停在那边用户看完结果再自行关闭在循环型程序里提供一个像“输入q退出”这样的选项如果用户多次输错程序不能无限循环卡死通常限制重试次数比如连续输错三次就友好地结束。反过来“重来”也很重要。用户做完一次操作之后可能马上想做第二次这时候程序应该在结束前问一句“再来一次吗”而不是让用户重新双击文件。前者叫可用性后者叫折磨人。虽然只是加一个while True外壳的事但在“能不能拿给别人玩”这件事上差别巨大。3.4 读码关代码是给别人读的第四关可能有点反直觉但我要说给别人用的程序不仅要照顾使用程序的人还要照顾看代码的人。这个人可能是未来的你也可能是别的同学。老师发下来的项目一周后你回头看自己写的代码如果变量全是a、b、c缩进乱七八糟注释一句没有那谁也看不懂包括你自己。编程界有一句被说烂了的话代码是写给人看的只是顺便能在机器上跑。这话放在少儿编程里并不过分。变量名要能表达含义比如score就是成绩total就是总数而不是x和y满天飞重要的逻辑要加注释说明“这里在做什么”“为什么这么做”一个有完整功能的步骤尽量封装成函数起一个清晰的名字。我在课上还加了一条“命名联想练习”你写完一个变量闭上眼想象一个从没见过这代码的同学他看到这个名字能不能猜出它存的是什么。猜不出就换个更具体的名字。这个过程就是程序员的“换位思考”跟用户视角本质上是一回事。3.5 环境关在别人电脑上能跑起来第五关是环境关。很多孩子在自己的电脑上跑代码一帆风顺因为环境是自己配好的发给同学之后对方电脑上没装Python或者装的是不同的版本程序一运行就报错。给别人用的程序包装和代码同样重要。最基础的要求是写一份运行说明说清楚“需要装什么软件、什么版本、怎么运行这个文件”。稍微进阶的做法是如果程序依赖第三方库需要额外提供安装指令。再进一步如果真想把程序发给完全不懂编程的小朋友玩可以考虑打包成可以直接双击运行的形式虽然对Python课程来说还不是常规操作但至少要让孩子们意识到程序不只是能跑还要能在别人的机器上跑。环境关还有一个很容易被忽略的小点文件名和路径。如果程序文件存放在一个中文路径的文件夹里部分环境可能会出问题。我在课堂上把这个叫“玄学问题”并不是每次都会发生但一旦发生就非常难排查。越早养成“路径和文件名尽量用英文”的习惯以后越省心。3.6 测试关找真人试一下最后一关是测试关也是我认为最有价值的一关。写程序的人天然有一种“信息优势”你太熟悉自己的代码了所以根本不会做出那些让程序崩溃的操作。可用户不是这样。想真正知道程序“给别人用”靠不靠谱只有一个黄金法则找一位完全不了解你代码的人让他上手试。测试的时候写代码的人唯一要做的事情就是闭嘴。你站旁边看着不要提示不要提前说“这里要小心”不要抢过键盘帮他操作。看他在哪一步犹豫了、在哪一步输错了、在哪一步看不懂提示语然后把这些“卡壳点”全部记下来。回去之后针对每一个卡壳点改程序。他自己摸索出来的用法比你说的任何话都管用。很多孩子试完回来跟我说同桌问他“这个程序要不要先双击图标双击哪个”之类的问题他之前完全没想过。这种反馈极其珍贵——你那句“按回车继续”在你自己脑子里无比清晰但在对方眼里可能就是无字天书。找人试一次胜过自己检查十遍。4. 课堂实测中最容易翻车的五个问题4.1 程序一运行就闪退给孩子调试程序时最高频的问题就是“一闪而过”双击运行Python文件程序瞬间弹出又瞬间消失根本来不及看结果。在绝大多数情况下原因都是程序运行到末尾所有代码执行完窗口就被系统自动关闭了。怎么办很简单在程序最后加一行input(按回车退出)让程序在结束前停下来等用户敲一下回车。这个细节看起来小但对“给别人用”来说至关重要。你自己写代码时是在编辑器里运行的窗口关闭不关闭你都无所谓可对方是双击运行的闪退的窗口意味着他根本不知道程序有没有正常工作。如果你想让自己写的程序显得“像个正经软件”这行input是最便宜的入门装。4.2 输入了中文直接报错或乱码第二个高频问题是中文乱码。这个问题的根源多半不在代码而在运行环境的编码配置。Python 3的源码默认是UTF-8编码但Windows传统的控制台窗口默认代码页可能是GBK两边编码不一致打印中文时就有概率出现乱码或者直接报UnicodeDecodeError这类错误。从少儿编程的角度我不会让孩子去改复杂的系统设置只给出两条实际可行的建议一是在代码文件头部写上# -*- coding: utf-8 -*-虽然Python 3里这行不是必须的但能帮着规避一部分编码问题也让孩子养成声明编码的好习惯二是在课堂上统一用同一个编辑器或开发环境比如VS Code或Thonny把编码配置好之后全班统一出现此类问题的概率就低很多。顺带说一句等孩子以后接触文件读写时编码问题还会再次出现到时候“先声明编码、再处理文本”就会自然变成他们的肌肉记忆。4.3 一输错就卡死在循环里循环是少儿编程的一道坎用while True设计输入重试之后新的坑很快浮出水面程序陷入死循环用户无论怎么操作都跳不出来。最常见的死循环原因有几种循环条件写成了永远为真的表达式break放错了位置放在continue后面永远执行不到循环内部修改条件变量的语句被continue跳过还有一种是忘记给用户提供退出选项一旦进入循环就只能强制关窗口。我教孩子排查这类问题时会让他们在草稿纸上画“流程跟踪图”把每一次循环的进入、判断、退出分支写出来。这是最朴素的调试方法但对刚接触循环的孩子非常有效。画完之后通常一眼就能看出哪个分支漏了break哪个条件写反了。另外从“给别人用”的角度我强烈建议在进入循环前先告诉用户“输入q可以退出”并且真的去实现它而不是只当作一句摆设。4.4 数字校验只做了一半第四个问题非常隐蔽很多大人也经常犯程序确实做了try...except把非数字输入挡住了但没有做范围校验或者做了范围校验却没有处理空输入。比如成绩判断器用户直接按回车int()同样会触发ValueError用户输入-5int()能正常转换但成绩显然不可能为负用户输入150同样通过了类型转换但它不是真实成绩。完整的数字输入校验应该是类型校验、范围校验、空输入校验三层全部做完。我在课上会要求孩子们在写完输入逻辑后自己扮演一个“捣乱用户”把能想到的非法输入全部怼一遍空字符串、负号、小数、超大数、中文、英文、带空格、带百分号。每条都不崩溃程序才算真正过了输入这一关。这个过程看起来像是浪费时间但其实是最有效的健壮性训练。4.5 代码在自己电脑能跑别人电脑上不行最后一个问题是环境差异问题。同一个.py文件在A同学的电脑上正常运行发给B同学之后报错这种情况在课堂上屡见不鲜。可能的原因五花八门A用的是Python 3.11B用的是Python 2.7两边的print语法都不一样A的电脑上装了第三方库但B没装A的代码文件保存在纯英文路径下B保存到了中文路径里甚至A用的WindowsB用的是macOS路径分隔符都不一样。从这一课的角度我给孩子立了一条规矩你的程序想要发给别人就必须附带一个“使用说明”至少写清楚三件事——程序需要哪个版本的Python、是否需要安装额外的库、怎么启动这个程序。写不出来说明说明你自己也没搞清楚运行条件。这不是额外负担这就是“给别人用的程序”的一部分。很多复杂的软件比如大家听过的小程序商城、各类网站后台本质上那些开发者天天解决的问题跟孩子们在这里遇到的问题是一回事别人怎么用、环境怎么配、出错了怎么处理。5. 课后练习设计一个别人会用的“班费计算器”5.1 练习要求拆解课程最后我布置了一个综合练习让孩子们做一个“班费计算器”需求是输入班级人数和每人交的班费金额计算并输出总共收到多少钱。如果只看功能这个程序用一个乘法就写完了没什么意思但加上“给别人用”的要求之后难度立刻上来了。我给练习附加了五条约束。第一条人数和金额都必须是大于0的数字输入任何非法内容都不能崩溃第二条用户输错之后要能重新输入而不是程序退出第三条算完一次之后要能继续算第二次还要有明确的退出方式第四条提示语要清楚到让一个从没见过这个程序的同学也能直接操作第五条找一个同学来试你的程序记录他卡壳的地方然后针对每个卡壳点修改程序并在代码注释里写出你改了哪一处、为什么改。这五条约束没有一条涉及新语法全部都在练这一课的核心把自己从“写代码的人”切换到“用程序的人”。也只有这样一个本来十几行就写完的练习才逼着孩子们去思考真正有用的东西。5.2 参考思路和代码骨架限于课时我没有强行要求全部孩子一步到位但给了一个参考骨架。核心思路是把“读一个合法数字”这个动作封装成通用函数——传入提示语、最小值、最大值返回一个合法数字。这样主程序就是两段清晰的逻辑先读人数再读金额最后输出总额。参考代码如下def read_number(notice, min_value): while True: s input(notice) if s.lower() q: return None try: n float(s) except ValueError: print(请输入数字) continue if n min_value: print(f输入的数字不能小于{min_value}) continue return n while True: print( 班费计算器 ) count read_number(请输入班级人数q退出, 1) if count is None: break money read_number(请输入每人班费金额q退出, 0.01) if money is None: break print(f总共收到 {int(count)} 人每人 {money:.2f} 元合计 {count * money:.2f} 元)这里用了float()而不是int()因为钱是小数min_value的传参让函数可以复用到不同的数字读取场景f{money:.2f}是格式化字符串保留两位小数跟现实中的“钱”一致。对还没学到这些写法的孩子我会告诉他们先按自己会的写这个骨架当作参考即可重点是那五条约束必须全部做到语法是实现方式的问题。5.3 怎么判断孩子做得“够不够好”课后如何评价这个练习我给学生一个自查表每一条都是“能不能给别人用”的直接体现程序被同学测试10次有没有一次崩溃非法输入全面测试过没有包括负数、0、小数、字母、中文、空输入、超长数字提示语有没有说清楚“该输入什么、当前发生了什么、接下来做什么”用户能不能在需要时随时退出代码里有没有清晰变量名和注释有没有记录同学试用时的反馈并真的改了程序。最重要的标准是最后一条这个程序同桌用完之后有没有说一句“还挺好用”。这句话比代码行数、比用到了多高级的语法都有价值。我也提醒家长和老师不要急着用成人的标准去评价孩子的作品看到“班级人数是1.5人也通过了”这种bug时先别嘲讽先肯定他发现问题的过程再引导他想“为什么人数必须是整数”这种问题本身。6. 一个小经验让用户替你找bug最后分享一个我自己的小经验。作为老师我每次给学生发操作类小程序之前都会先找办公室不太懂技术的人试一遍。他们不会代码也不懂程序逻辑但恰恰是这种“无知”最能暴露程序里那些想当然的地方。他们每问一句“这里什么意思”我就知道程序里哪句话没写清楚他们每操作错一次我就知道哪里缺了防呆设计。这比我一个人闷头检查代码高效得多。这个思路平移给孩子用就是让他们把作品发给同桌、发给爸妈、发给任何一个“看不懂代码”的人。别站在旁边解释就看着他们用记录他们在哪里卡住。他们的每一次卡壳都是一条免费的bug报告。你改完之后再让他们试慢慢你就会发现他们提的问题从“这是什么”变成“能不能加这个功能”——这说明你的程序已经跨过了“能用”的门槛开始被人认真当作一个工具来使用了。这一课没有什么新语法但几乎所有孩子上完之后的共同感受是原来写代码最重要的不是让机器听懂而是让人用明白。把这个想通了后面不管学图形界面还是写更大的项目思路都会顺很多。