ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

少儿编程第29课:如何让你的Python程序真正“给别人用”

少儿编程第29课:如何让你的Python程序真正“给别人用” 这一课是孩子们第一次意识到“写程序”和“写软件”是两回事。前面28课我们学了变量、分支、循环、函数、列表、字典孩子们已经能用这些语法拼出一个能跑的猜数字游戏自己玩得挺开心。可当我在课堂上说“把你的程序发给同桌试一下”的时候教室里立刻就炸开锅了。有孩子跑过来说“老师我同桌输入了字母m程序就崩了”另一个孩子举手“老师我把代码发给我妈她电脑上显示没有Python根本打不开。”这就是典型的分水岭。自己写代码、自己运行代码怎么折腾都行一旦程序要交给别人用很多平时看不见的问题就会全部冒出来。这堂课没有新语法只有一套“换位思考”的开发习惯如何让你的程序扛得住乱输入、看得懂报错、离得开开发环境以及如何让另一个人类愿意继续使用你写的东西。这篇文章就围绕第29课的完整思路来写既是一份课堂教案也是一份少儿编程项目从“自娱自乐”走向“可用作品”的实操指南。不管是带孩子学编程的家长还是正在自学Python的初学者都能从中拿到可以直接照抄的代码、步骤和避坑清单。1. 为什么“给别人用”是编程的分水岭1.1 从自己吃到请客吃饭我习惯用一个生活类比来开场自己做饭自己吃煮糊了、咸了、盘子没洗干净都能咽下去顶多自己难受一下。但如果是请同学来家里吃饭你至少得考虑对方忌口不忌口、桌上有没有公筷、菜凉了怎么加热。程序也是一样的道理自己写的程序输入怪一点、界面丑一点、直接报错一串英文都无所谓可当这个程序要交给爸妈、同桌、甚至一个完全陌生的人用时使用者不会关心你内部怎么实现他们只会在意三件事能不能打开、操作顺不顺手、崩了有没有人告诉我要怎么补救。“给别人用的程序要注意什么”这个项目标题本质上问的就是这套用户思维。它不是语法课而是工程习惯课。很多孩子代码写得没问题一但脱离自己的电脑就“见光死”根因就在于写代码的时候只想着“我要实现什么功能”从没想过“别人会怎么使用我的功能”。1.2 使用者的三种“不按套路出牌”上课时我会把使用者想象成一个“什么都不知道、还喜欢乱点”的人总结下来有三种典型操作专门用来考验程序作者第一种是乱输入。明明让输入数字使用者偏偏输入字母、小数、负数甚至什么都不输直接按回车。如果你直接int(input())程序当场崩溃使用者只会看到一行红色报错然后得出结论“这个程序有问题。”第二种是乱双击。使用者拿到代码文件不一定知道要用命令行运行。他们可能直接双击.py文件或者干脆发到手机上打开发现满屏的代码就傻眼了。这说明如果只给对方源码对方根本没有运行的义务一切使用门槛都应该由开发者来消除。第三种是乱改东西。使用者可能把程序文件和数据文件放错目录可能改了文件名可能电脑上根本没装Python。所有你在自己电脑上“理所当然”存在的条件在对方电脑上统统不能假设。所以这堂课的第一个作业不是写新程序而是把以前写的旧程序拿出来扮演一次“使用者”故意乱输入、乱操作看看自己的程序会不会崩。这个体验比任何说教都来得直接。2. 输入处理程序的第一道防线2.1 输入校验try/except 是程序的安全带“给别人用的程序要注意什么”最容易出问题的地方就是输入。Python 的input()返回的一定是字符串想拿它当数字用必须转换。这个转换就是最脆弱的环节。我先在黑板上写一段典型的反面教材age int(input(请输入你的年龄))这行代码在用户老老实实输入“12”的时候没问题但只要用户输入“abc”程序立刻抛出一个ValueError直接中止。对使用者来说这就是“程序坏了”至于什么是 ValueError他们根本不想知道。解决办法是给这段代码系上安全带try: age int(input(请输入你的年龄)) except ValueError: print(你输入的不是一个数字请重新输入。)try/except可以理解成过马路之前先左右看先尝试往前走发现有车异常就停下来处理而不是被撞了才后悔。这是Python提供给初学者的第一个“容错武器”从这一课开始才算真正进入实战。不过这里有个细节要提醒光有try/except还不够你只是拦截了错误程序依然结束了。对使用者来说输入一次错了就要重新启动程序体验还是差。所以更完整的做法是配合循环让用户重新输入这个放到后面第3章展开。2.2 边界条件比“数字”更重要的是“合理的数字”除了要防止用户输入非数字还要防止用户输入“合理格式但明显不合逻辑”的数字。比如年龄输入-5分数输入999身高输入2米5BMI计算器输入体重0.1公斤。这些值在类型上都是数字int()不会报错但程序算出来的结果毫无意义。这就是边界检查。我上课经常用的例子是BMI计算器height float(input(请输入身高米)) weight float(input(请输入体重千克)) bmi weight / (height ** 2) print(f你的BMI是{bmi:.1f})如果用户输入身高0程序直接给你一个ZeroDivisionError如果输入负数算出来的BMI也是负数使用者看到会以为自己身体出毛病了。所以正确的做法是在转换之后立刻检查数据的合理性不满足条件就要求重新输入。我建议把“获取一个合法数字”的逻辑封装成一个函数这正是给初学者的第一个“工具函数”思维def get_positive_number(prompt): while True: text input(prompt) try: num float(text) if num 0: return num print(数字要大于 0 哦请重新输入。) except ValueError: print(这不是一个数字请重新输入。) height get_positive_number(请输入身高米) weight get_positive_number(请输入体重千克) bmi weight / (height ** 2) print(f你的BMI是{bmi:.1f})这个函数一次性解决了两类问题非数字输入和不合逻辑输入。而且只要写一次以后在任何程序里都能复用。孩子在写的时候可能没感觉但等他们做了三五个“给别人用的程序”之后就会发现没有这个函数每个程序都要反复写一堆重复的校验代码。2.3 提示信息把话说清楚提示信息是“给别人用的程序”最容易忽略的细节。很多初学程序写的提示是“请输入”使用者根本不知道要输入什么、有什么范围、输错了怎么办。一个友好的提示应该把事情说全糟糕的提示友好的提示请输入请输入你的年龄1~120之间的整数输入错误你输入的不是数字请重新输入一个整数。程序结束谢谢使用按回车键退出。报错一大堆英文程序遇到问题数据文件缺失请把本程序和数据文件放在同一个文件夹里。课堂上我会让孩子们两个人一组互相读一遍对方程序里的每一条print读完就问一句话“这句话我看得懂吗”很多人当场就笑了因为发现自己写的提示只有自己懂。语言越具体使用者的困惑就越少找你来问问题的次数也越少。3. 增强程序友好性的实用技巧3.1 循环重试犯错之后给第二次机会上一节说的get_positive_number已经在用循环了这里单独拎出来强调一下设计原理。人的操作不可能100%正确程序对使用者要有一点“宽容度”。真正好用的程序不是永远不出错的程序而是出错之后能让人轻松恢复的程序。我给出的推荐模板是while True加break的组合while True: text input(请猜一个 1~100 之间的数字) if not text.isdigit(): print(请输入数字别输字母。) continue guess int(text) if 1 guess 100: break print(数字要在 1 到 100 之间。)continue在这里的作用是“跳过本轮直接回到输入”相当于告诉使用者错了没关系再试一次。这种反复尝试直到成功的循环模式几乎可以套用到所有输入场景菜单选择、登录密码、游戏设置全部通用。有一个小细节值得注意无限重试有时候反而会让人烦。比如登录类程序试错三次就应该退出猜数字游戏则可以一直玩。所以循环不是越多越好而是要根据使用场景决定重试上限。给初级学员讲的时候我会说想一想如果使用者故意乱操作100次你的程序是被他玩死还是温柔地一直陪他玩没有标准答案但要学会做选择。3.2 数据保存程序要有“长记性”“给别人用的程序”还有一个隐性需求数据要能留下来。孩子自己写程序关掉窗口就完了无所谓的但如果程序要给别人用游戏最高分、记事本内容、作业完成记录这些数据如果关掉就丢使用者一定会很失望。这里用到文件读写正好是第29课适合引入的内容。我以一个猜数字游戏的最高分记录为例def read_best(): try: with open(best_score.txt, r, encodingutf-8) as f: return int(f.read()) except FileNotFoundError: return None def save_best(score): with open(best_score.txt, w, encodingutf-8) as f: f.write(str(score))read_best里那个try/except FileNotFoundError非常关键程序第一次运行时并没有best_score.txt这个文件如果直接读就会报错崩溃。真实项目里程序要“容忍”第一次运行的状态而不是假设所有文件都已经存在。这也呼应了前面说的永远不要假设使用者的环境和你一样。文件保存的位置也要留心。如果代码用相对路径best_score.txt那程序运行时会以“当前工作目录”为基准找文件。把程序发给别人后如果对方不是在同一个目录下运行依然可能找不到文件。给初学者的建议是程序、数据文件务必放在同一个文件夹里并且写清楚使用说明。等以后学到pathlib再用更稳妥的方式处理路径。3.3 菜单与界面让程序一眼就会用“给别人用的程序”通常需要一个菜单。菜单的本质是降低使用门槛使用者不用记命令只要按数字选就行。一个干净的文字菜单是这样设计的def show_menu(): print(\n 猜数字游戏 ) print(1. 开始游戏) print(2. 查看最高分) print(3. 退出程序) def main(): while True: show_menu() choice input(请选择1/2/3).strip() if choice 1: play_game() elif choice 2: show_best() elif choice 3: print(感谢使用再见) break else: print(没有这个选项请输入 1、2 或 3。)这个循环菜单有几个细节值得讲。第一用while True包住整个菜单选择“退出”时才break这样使用者在玩完一局后还能回到菜单而不是退到系统命令行。第二choice比较的是字符串而不是数字因为input()返回的就是字符串没必要为了比较先转成int还能避免“输入字母崩溃”的问题。第三对非法选项要给出明确提示并且让使用者重新选择而不是直接退出。输出格式同样影响使用体验。我经常跟孩子们说你写出来的程序输出就像房间的摆设。满屏的数字挤在一起观感极差。用 f-string 的格式化很容易就能改善print(f{name:10}{score:5}分)10表示左对齐占10个字符宽5表示右对齐占5个字符宽。这样打印出来的成绩单列是整整齐齐的看起来像模像样。这些细节单独拎出来都不难难的是在写程序时真的有“对方在看”的意识。4. 代码结构让别人也能接手4.1 函数化拆分一个函数只做一件事“给别人用的程序”不只是给最终用户用也会给你自己、给未来接手的人看。很多初学者的程序是“一坨式”的几百行代码从第一行写到最后一行没有函数没有分割注释全靠回忆。这种程序哪怕功能是对的维护起来也要命。我从第29课开始强制要求孩子们做一件事每个程序至少拆出三个函数。猜数字游戏就可以这样拆def generate_answer(): return random.randint(1, 100) def play_game(): answer generate_answer() count 0 while True: guess get_guess() count 1 if guess answer: print(太大了) elif guess answer: print(太小了) else: print(f猜对了你用了 {count} 次。) return count def main(): while True: choice show_menu() if choice 1: score play_game() update_best(score) elif choice 2: show_best() else: break每个函数都只做一件事play_game负责游戏逻辑update_best负责更新记录main负责调度。好处很明显调试时只用看一个函数的局部逻辑不用在整片代码里找变量想加功能时往menu里加一个分支就行不用碰核心逻辑。这个“一个函数只做一件事”的原则是程序能不能长大的分水岭。我见过太多孩子写着写着把自己绕晕主要就是因为所有逻辑都堆在一起变量满天飞自己都分不清哪是哪。拆成函数之后大脑负担瞬间小了很多。4.2 变量命名与注释写给三个月后的自己给学生改程序时最常见的坏习惯是变量名乱起a、b、c、tmp、data1。单独看一行代码谁都不知道这个变量是干嘛的。我自己写代码时有个原则变量名要能读出来让一个陌生人看到名字就能猜出用途。糟糕命名良好命名说明auser_age用户年龄读起来就知道含义bbest_score历史最高分tmptemp_sum临时总和有明确来源fcalculate_bmi函数名用动词开头表示行为命名之外注释也是一个容易被忽略的点。我告诉孩子们注释不是给老师看的而是写给“三个月后的自己”看的。因为三个月后你再打开这段代码大概率已经想不起来当时为什么这么写了。注释要写“为什么”而不是“是什么”。比如# 为什么这里要加 try因为用户第一次运行程序时根本没有这个文件 def read_best(): try: with open(best_score.txt, r, encodingutf-8) as f: return int(f.read()) except FileNotFoundError: return None这种注释记录的是设计决策而不是重复代码。孩子以后看到这段注释能瞬间明白当初的意图这就是好注释的价值。4.3 模块入口if __name__ __main__的妙用很多初学Python的人见过下面这行代码但不知道为什么要写if __name__ __main__: main()这行代码的意思是只有当“这个文件是被直接运行时”才执行main()如果这个文件是被其他程序当模块导入的就不会自动执行。放到“给别人用的程序”场景里它的作用就是保护别人可以import你的代码、复用你的函数但不会一导入就弹出你的菜单界面也不会立刻运行你的逻辑。孩子写到这里我通常会说一句话从今天起你的程序不再只是一堆命令的排列而是一个可以被别人借用的工具箱。只要通过函数把能力暴露出去别人就能拿你的函数拼出新的程序。这种“模块化思维”对将来的任何编程语言都有用。5. 程序打包与发布让别人双击就能用5.1 为什么不能直接发.py文件“老师我把程序发给我妈她电脑上显示没有Python。”这是每次上这堂课都会出现的问题。Python 程序要在别的电脑上运行前提是那台电脑安装了Python解释器版本还要兼容。让使用者去装Python、装依赖库对大多数非程序员来说门槛太高而且容易被劝退。所以“给别人用的程序”最好打包成可执行文件。Windows 下就是.exe双击就能运行对方完全不需要关心Python是什么也不用装任何东西。这个过程叫“打包”本质上是把Python解释器、你的代码、用到的库全部塞进一个可执行文件里。对少儿编程课堂来说打包还有一个额外的好处强烈的成就感。孩子把一个半成品.py变成一个带图标的“软件”双击就能玩那种感觉完全不一样。它会让孩子们觉得自己做的真的是一款产品而不只是课堂作业。5.2 用 PyInstaller 打包步骤与参数Python 最常用的打包工具是 PyInstaller。安装和打包流程都相当简单我给孩子们的操作步骤只有三条pip install pyinstaller装完之后在命令行里进入代码所在目录执行pyinstaller -F guess_number.py-F表示打包成单个文件所有依赖都塞进一个.exe里方便直接发给别人。打包完成后在dist文件夹里就能找到guess_number.exe。关于-w参数这里必须说清楚。我之前看到不少教程一上来就是pyinstaller -F -w xxx.py但-w的意思是“不显示命令行窗口”它适合GUI程序比如用 tkinter 写的界面程序。如果我们的程序本身就是文字交互的用了-w之后运行起来没有控制台窗口输入输出全看不见游戏根本没法玩。所以纯命令行程序只用-F就够了等做了图形界面程序再考虑加-w。打包速度通常几十秒到几分钟不等取决于代码里引入的库有多大。打包完成后我建议做一次“干净环境测试”把生成的.exe复制到一个全新的文件夹里双击运行看能不能正常工作。这个测试很关键因为打包时可能引用了本地机器上的资源复制到别处就会出问题。5.3 打包后的常见坑杀毒软件、图标和体积打包这个环节我踩过的坑比写代码本身还多这里挑几个一定要提前知道的。杀毒软件误报是最常见的问题。PyInstaller 打包出来的程序自带Python解释器行为模式和正常程序不太一样有些杀毒软件会把dist目录里的.exe误判为病毒。这不是代码写错了而是打包工具的特性。解决办法是在自己电脑上先杀毒扫描一遍确认没问题发给别人时如果对方杀毒软件报警可以解释这个情况让对方向杀毒软件添加信任。还有图标问题。默认生成的.exe图标是 PyInstaller 的通用图标没有辨识度。如果要换成自己的图标需要在打包命令里加--icon参数pyinstaller -F --iconmy_icon.ico guess_number.py注意图标文件必须是.ico格式普通的.png不一定能被设定为应用图标。对孩子们来说加一个自己画的图标程序的“产品感”立刻上一个档次。打包体积问题也要有心理预期。一个“Hello World”级别的Python程序打包成单文件后也有 5~10 MB。这是因为里面塞了整个Python运行时。有些孩子第一次打包后会惊呼“为什么这么大”这是正常现象不用担心也跟程序本身好坏无关。6. 课堂实操与问题排查实录6.1 课堂活动给“别人的破程序”挑错第29课的课堂活动我设计成“破程序挑错”比赛。我会提前准备一个故意写了一堆问题的Python程序让孩子两两一组扮演“使用者”去操作把发现的问题写在纸条上。程序里埋好的坑大概是这些直接int(input())输入字母直接崩溃年龄输入-5也照单全收不会检查边界菜单选择输错直接退出而不是重新提示文件不存在时不处理第一次运行就报错变量名全部是a、b、c代码难读没有任何注释没人知道每个函数是干嘛的孩子们通常在五分钟内就能把前三个问题找出来但后面两个需要引导。这个活动的意义在于检验一个程序是否“给别人用”最快的办法就是把它丢给一个完全陌生的使用者然后闭嘴看他能不能自己用起来。作者亲自测试永远是无效的因为作者太了解程序会自动跳过很多让使用者困惑的地方。6.2 常见问题速查表这些年在少儿编程课里遇到的高频问题基本可以汇总成一张速查表方便孩子和家长对照排查现象原因解决办法输入字母后程序崩溃没有用try/except处理转换异常用whiletry重试参考第2章的get_positive_number第一次运行读文件报错文件不存在时没处理用try/except FileNotFoundError文件不存在时返回默认值发给别人打不开对方电脑没有Python环境用 PyInstaller 打包成.exe再交付打包后文字输入没反应用了-w参数隐藏了控制台纯命令行程序不要加-w中文乱码文件编码问题使用open()时指定encodingutf-8程序闪退看不到报错异常导致程序直接退出在最外层加try/except输出错误信息并暂停打包后被杀毒软件报警PyInstaller打包特征被误判本机扫描确认安全向对方说明并添加信任这里特别要提醒第一次接触打包的人如果双击.exe后闪退一个很实用的排查方法是先在原Python环境里直接运行一遍同样的操作看有没有报错。打包本身不会修复代码bug只是换了一个运行外壳。先把代码调试好再打包顺序不要反。6.3 调试基本功打印观察与交付前清理最后说一个很基础的调试方法虽然简单但对孩子来说极其实用就是print观察法。程序出问题时不要瞎猜先加一行打印看看变量到底是多少def play_game(): answer generate_answer() count 0 while True: # print(调试信息答案是, answer) # 测试完记得删掉 guess get_guess() ...这行被注释掉的调试print玩游戏时能看到内部答案能帮你立刻判断逻辑对不对。很多孩子遇到“为什么我猜对了还说不对”的问题时加上这一行打印疑问马上就解决了。但这里有一个必须强调的习惯交付之前所有调试用的打印一定要清理干净。给别人用的程序里不能全是“测试一下”“这里有问题”之类的输出那会严重干扰使用者体验。我会在课堂上要求孩子们交作业前过一遍自己的代码把注释掉的调试代码删除这个习惯比编程本身更值钱。调试还有个小技巧让另一个人来“读”你的代码。当你解释给别人听、讲到某一步卡住的时候那个卡住的地方通常就是出问题的地方。这个经验在我自己写代码时屡试不爽用在孩子身上也特别有效。回到这堂课的主题“给别人用的程序要注意什么”说白了就是四个词校验输入、友好提示、数据保存、独立可运行。这些并不是多难的语法每一个单独拿出来都够简单难的是在写程序的每一刻都把自己当成那个“什么都不知道的使用者”。我在教学里最大的感受是孩子们不是学不会语法而是很容易只看见代码看不见代码对面那个人。一旦把视角转过去程序的质量会肉眼可见地上一大截。最后再多说一句每个孩子写完程序都该找一个同学来盲测一下自己站在旁边一句话不说看对方能不能自己玩明白。能通过这个测试的程序才算真正“给别人用了”。
返回列表