Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

前言

写作背景

2020年,突如其来的疫情让整个世界按下了暂停键。居家隔离的日子里,我想给家里10岁的孩子找点有意义的事情做。考虑到编程既能帮孩子巩固和应用数学知识、培养逻辑思维,又充满创造乐趣,于是我决定教他学编程。

为什么选择Python?因为它简单易学,相比别的编程语言更有乐趣,又能下探到计算机的基础知识。对于孩子来说,Python就像一把入门的钥匙,能快速打开编程世界的大门。

为什么选择Python而不是Scratch?

很多家长会问:现在不是流行用Scratch教孩子编程吗?为什么选择Python?

这是一个很好的问题!Scratch确实有其优点,但对于小学高年级(3年级以上)及以上的孩子来说,Python是更好的选择。

真实的编程体验

  • Scratch使用积木块拼接,更像是玩游戏,虽然有趣但距离真实的编程环境较远
  • Python让孩子直接接触真实的代码和编程工具,了解什么是计算机程序命令行开发工具以及基本的计算机体系结构

经过实践验证,小学高年级的孩子完全有能力接受这些概念,而且他们会对能深入的控制计算机感到自豪!

更深入的概念理解

  • Scratch隐藏了太多细节,孩子可能只学会了拖拽积木,但不理解背后的软件和计算机体系结构原理
  • Python虽然需要写代码,但每一步都是透明的,孩子能真正理解程序是如何一行行执行的、变量是如何存储和传递数据的、语法错误是什么以及如何调试

更好的知识迁移

  • Scratch的积木编程方式相对独特,学完后如果要转其他语言需要重新适应
  • Python学到的编程思维和语法知识,可以轻松迁移到其他编程语言(如JavaScript、C++等)

更大的成长空间

  • Scratch更适合编程启蒙(幼儿园到小学低年级)
  • Python可以陪伴孩子从小学到高中,甚至大学和工作都能用到;孩子可以用Python做真正有用的项目(数据分析、网站开发、自动化工具等)

💡 给家长的建议

如果您的孩子:

  • 年龄在8岁以下,Scratch可能是更好的起点
  • 年龄在10-12岁(小学高年级),完全可以直接学Python
  • 已经学过Scratch,现在可以挑战Python,进入真正的编程世界

这本书适合谁?

这本书是同时为家长和孩子设计的,它既可以是家长的备课教案,也可以是孩子的自学课本

对于孩子

  • 年龄:小学高年级(10-12岁)
  • 数学基础
    • ✅ 掌握基本的数学运算(加减乘除)
    • ✅ 了解未知数和方程的概念
    • ✅ 会解简单的应用题
  • 编程基础:零起点,不需要学过编程
  • 学习方式
    • 在家长指导下学习
    • 独立阅读和复习
    • 参考书中的例题和练习

对于家长

  • 数学基础
    • ✅ 具备高中数学知识
    • ✅ 学习过函数的概念
    • ✅ 如果家长本身会编程就更好,但不是必需的
  • 电脑操作能力:具备基本的电脑操作能力即可
  • 学习方式
    • 作为教学参考和备课手册
    • 陪伴孩子学习,解决环境、安装和出错调试等问题
    • 如果不会编程,可以先自学、操作和练习,然后再给孩子讲解教学

💡 这本书的核心目的

这本书希望通过编程教学,共建亲子关系——家长陪伴和引导孩子的成长,通过编程的创造力一起体验学习乐趣。

如果家长不会编程,也没关系!这可以变成一项共学活动:家长先自学掌握,然后教孩子。这反而会让孩子看到“爸爸妈妈也在学习“,更有激励作用!

编写原则

📝 符合儿童认知规律

考虑到小学高年级阶段孩子的知识结构特点,在内容编排上:

  • 生活化引入:每个概念都用生活化的例子来引入
  • 循序渐进:从简单到复杂,做好足够的逻辑台阶搭建
  • 自然衔接:避免突然跳跃,让知识点自然衔接
  • 类比教学:大量使用类比和比喻(比如把变量比作盒子)
  • 详细说明:提供详细的步骤说明和注释
  • 梯度练习:设计逐步增加的练习难度
  • 适度扩展:在合适的上下文下适度扩展一些软件设计和计算机体系结构知识

👨‍👩‍👧‍👦 双重视角设计

这本书既可以作为家长的教学手册,也可以作为孩子的参考书:

  • 对孩子:用生动有趣的语言讲解,配上大量练习
  • 对家长:提供教学建议(Tips 标注的板块),指出易错点和教学技巧

📐 数学与几何知识的复习和应用

  • 复习和巩固小学数学的各种知识,如数学运算和应用题、几何应用、统计概念等…
  • 通过编程为数学知识找到一个应用的场合,让学校的知识能够和解决具体的问题关联起来

🎮 趣味性与实用性结合

  • 练习题目贴近孩子的生活(计算图形、课表管理、小游戏)
  • 从基础的命令行程序,到图形界面,再到数据报表
  • 让孩子看到编程的实际用途,保持学习动力

使用指南

学习路径

整个教程分为18章,按照以下逻辑递进展开:

第一阶段:入门基础(第1-5章)

  • 准备工作:安装Python和编程工具
  • 输入输出:了解程序如何与人交互
  • 变量类型:学习字符串、数字、布尔值

第二阶段:程序控制(第6-9章)

  • 顺序执行:理解程序的执行流程
  • 条件判断:学会让程序做决定
  • 循环语句:掌握重复执行的技巧
  • 流程图:用图形化方式理解程序逻辑

第三阶段:数据与行为组织(第10-12章)

  • 列表:管理多个相关的数据
  • 字典:用键值对组织信息
  • 函数:学习代码中的抽象和复用

第四阶段:综合应用(第13-16章)

  • 库的使用:学会使用现成的工具
  • 命令行程序:制作实用的脚本工具
  • 报表程序:用图表展示数据
  • 图形界面:创建可视化的应用程序

第五阶段:知识扩展(第17-18章)

  • 程序设计方法:了解所谓的软件设计和软件工程
  • 计算机体系结构:理解程序运行的物理硬件基础

如何使用这本书?

这本书建议的使用方式是:

第一步:家长先阅读

  • 家长完整阅读一遍教程,了解整体内容和结构
  • 理解各章节的知识点和难易程度
  • 提前熟悉要教授的知识,有针对性地备课

第二步:家长教孩子

  • 按照章节顺序,循序渐进地给孩子讲解
  • 演示代码的编写和运行过程
  • 引导孩子理解概念,而不是死记硬背

第三步:陪伴和解决问题

  • 帮助孩子解决环境配置、安装和出错调试等技术问题
  • 在孩子遇到困难时提供提示和引导
  • 鼓励孩子独立思考和解决问题

第四步:共建亲子关系

  • 通过编程教学,培养孩子的逻辑思维和创造力
  • 陪伴孩子在编程世界探索和成长
  • 一起体验编程创造的乐趣

💡 如果家长不会编程

这完全不是问题!您可以:

  1. 先自学:自己先跟着教程学一遍,完成练习
  2. 先操作:安装环境,编写代码,运行程序
  3. 先练习:自己动手做每个例题和练习
  4. 再教学:掌握后再给孩子讲解和教学

把它变成一项共学活动,孩子会看到“爸爸妈妈也在努力学习“,这反而会更好地激励孩子!学习过程本身就是最好的亲子陪伴。

给家长的教学建议

⏰ 合理安排时间

  • 每次学习1-2个知识点,不要贪多
  • 理论讲解与练习时间比例约为1:2
  • 鼓励孩子动手修改代码,观察变化

🎭 注重过程而非结果

  • 编程出错是正常现象,教会孩子调试
  • 让孩子解释自己的思路,培养表达能力
  • 即使有AI工具,亲自编程仍然是思维训练的好方法

🎯 因材施教

  • 根据孩子的接受速度调整进度
  • 兴趣是最好的老师,选择孩子感兴趣的练习
  • 鼓励创造性的改进和扩展

给孩子的学习建议

  • 多动手:编程是动手的技能,只看不练学不会
  • 多思考:理解为什么这样做,不要死记硬背
  • 多提问:不懂就问家长,或者记录下来一起查资料
  • 多创造:学完基础后,尝试做自己的小项目

关于AI时代的学习

有人可能会问:AI这么强大,还需要学编程吗?

这个问题我想了很久,目前给出的答案是需要的!

具两个例子:

  • 计算器很早就比人类在数学计算上强了,但是孩子依旧要学习数学,具备一定的计算能力。因为这是一种基本的能力培养,也是学习其它理科的基础;

  • AI 虽然很早就在象棋(后来是围棋)上战胜了人类,但是我们依旧会陪孩子下棋,通过下棋锻炼孩子的策略性思维,并享受下棋时候的亲子乐趣!

因此,我强烈建议,家长和孩子一起学习,然后亲自给孩子教编程,除了培养孩子逻辑思考、问题分解、抽象建模的能力,更是一个和孩子一起体验创造的乐趣的亲子时光!

在这个 AI 大行其道的时代,人类的学习重在过程,不在目的!学习编程的目的是了解数字世界运行的原理,培养思维能力和创造能力,而不是一定要成为程序员。

反馈与改进

如果你在使用过程中发现任何问题,或有改进建议,欢迎一起完善这份教程,帮助更多孩子走进编程的世界,理解这个世界。

第1章 安装环境

本章导读

在开始编程之旅前,我们需要先准备好“工具箱“。就像画画需要画笔和颜料,做手工需要剪刀和胶水一样,编程也需要专门的工具。

这一章我们将会:

  • 了解什么是编程语言和程序
  • 安装Python编程语言
  • 安装代码编辑器VS Code
  • 写出并运行我们的第一个程序!

什么是编程语言?

编程语言是什么?

想象一下,你想和一个不会说中文的外国朋友交流。你们需要一种共同的语言才能互相理解。同样的道理,我们想要让计算机帮我们做事,也需要用一种计算机能理解的语言——这就是编程语言

常见的编程语言有:

  • Python:简单易学,我们用它来入门
  • C语言:运行速度快,但较复杂
  • Java:功能强大,常用于开发大型软件
  • JavaScript:主要用于网页开发

什么是程序?

用编程语言写出来的一系列指令,就叫做程序。程序就像给计算机的一份详细说明书,告诉它要做什么、怎么做。

举个例子,如果你想让计算机计算两个数的和,程序就会告诉计算机:

  1. 接收第一个数字
  2. 接收第二个数字
  3. 把这两个数字相加
  4. 把结果显示出来

👨‍🏫 给家长的Tips

用“给机器人下指令“的类比来讲解,可以让孩子更容易理解程序的概念。可以和孩子一起讨论:如果我们要指挥一个机器人从教室走到操场,需要给它哪些指令?

计算机是如何工作的?

在学习编程前,让我们先了解一下计算机的基本组成。计算机就像一个人的身体,有五个主要部分在工作:

1. 输入设备(Input Device) - 就像人的眼睛和耳朵

  • 键盘:用来输入命令和文字
  • 鼠标:用来点击和选择
  • 麦克风:用来输入声音

2. 输出设备(Output Device) - 就像人的嘴巴和动作

  • 显示器:用来显示文字和图像
  • 打印机:用来打印纸张
  • 扬声器:用来播放声音

3. 内存(Memory) - 就像人的短期记忆

  • 临时存储程序运行时的数据
  • 关机后数据会丢失
  • 比如:你在做数学题时草稿纸上的数字

4. 硬盘(Hard Drive) - 就像人的笔记本

  • 长期保存文件和程序
  • 关机后数据不会丢失
  • 比如:你记在笔记本上的笔记

5. CPU(中央处理器) - 就像人的大脑

  • 执行程序中的指令
  • 进行计算和判断
  • 控制其他所有部件
flowchart LR
    A[输入设备<br/>键盘/鼠标] --> B[CPU<br/>中央处理器]
    C[内存<br/>Memory] <--> B
    D[硬盘<br/>存储] <--> B
    B --> E[输出设备<br/>显示器/打印机]

💡 给孩子的小知识

计算机的这五个部分就像一个团队在工作:

  • 输入设备把信息告诉CPU
  • CPU从内存中读取指令和数据
  • CPU处理数据,进行计算
  • CPU把结果发送到输出设备显示

我们编写的Python程序,就是存储在硬盘上,运行时被调入内存,然后由CPU一条条执行!

👨‍🏫 给家长的Tips

为什么需要安装Python?

你可能会问:计算机不是已经很聪明了吗?为什么还要安装Python?

答案是:计算机的CPU只能理解“机器语言“(由0和1组成的指令)。Python是一种“高级语言“,人类容易理解,但CPU不能直接执行。

Python解释器就像一个“翻译官“,它把我们写的Python代码翻译成CPU能理解的机器语言。这就是为什么我们需要先安装Python编程语言!

类比:就像你想和一个只会说英语的外国朋友交流,如果你们都说中文,就需要一个翻译官。Python解释器就是这个“翻译官“。

安装Python

Python是我们要学习的编程语言。现在让我们在电脑上安装它。

第一步:下载Python

  1. 打开浏览器,访问Python官网:https://www.python.org/

  2. 点击网站上的 “Downloads”(下载)按钮

  3. 网站会自动识别你的操作系统。我们以Windows 10为例:

    • 点击 “Downloads” 下拉菜单
    • 选择 “Windows”
    • 在下载页面找到 “Python 3.8.x”(稳定版本)
    • 点击下载

⚠️ 重要提示

教程中使用Python 3.8版本。如果你下载的版本号略有不同(比如3.9或3.10),也不用担心,它们的基本用法是一样的。

第二步:安装Python

  1. 找到下载好的安装文件(比如 python-3.8.10-amd64.exe),双击打开

  2. **非常重要!**在安装界面底部,有两个选项:

    • ☑️ Add Python 3.8 to PATH(把Python添加到系统路径)

    务必勾选这个选项!如果不勾选,以后使用会很不方便。

  3. 点击 “Install Now”(现在安装)

  4. 等待安装完成,可能需要几分钟

  5. 看到 “Setup was successful”(安装成功)后,点击 “Close”(关闭)

第三步:验证安装

让我们检查一下Python是否安装成功:

  1. Win + R 键,打开运行窗口

  2. 输入 cmd,按回车键,打开命令提示符

  3. 在黑色窗口中输入以下命令(按回车):

    python --version
    
  4. 如果看到类似这样的输出:

    Python 3.8.10
    

    恭喜你!Python安装成功了!

👨‍🏫 给家长的Tips

  • 什么是PATH? PATH是Windows的一个设置,告诉系统在哪里可以找到程序。勾选“Add to PATH“后,我们就能在电脑的任何位置使用Python,不需要每次都进入安装目录。
  • 如果忘记勾选PATH怎么办?可以卸载Python重新安装,或者手动添加到环境变量。建议重新安装,更简单。
  • 测试技巧: 可以让孩子在cmd中输入 python 然后按回车,如果出现 >>> 符号,说明Python交互环境已启动。输入 exit() 退出。

安装代码编辑器

虽然Python自带一个简单的编辑器(IDLE),但我们推荐使用更专业的工具:Visual Studio Code(简称VS Code)。它免费、强大,而且用起来很舒服。

第一步:下载VS Code

  1. 访问VS Code官网:https://code.visualstudio.com/

  2. 点击大大的蓝色 “Download”(下载)按钮

  3. 等待下载完成

第二步:安装VS Code

  1. 找到下载好的安装文件(比如 VSCodeUserSetup-x64.exe),双击运行

  2. 一路点击 “Next”(下一步)

  3. 建议勾选以下选项:

    • ☑️ Add “Open with Code” action to Windows Explorer(在右键菜单中添加“用Code打开“)
    • ☑️ Add “Open with Code” action to Windows Explorer file context menu(在文件右键菜单中添加“用Code打开“)

    这样以后我们想用VS Code打开文件或文件夹,只需要右键点击即可!

  4. 点击 “Install”(安装),等待完成

  5. 点击 “Finish”(完成)

第三步:安装Python扩展

VS Code安装好后,我们还需要告诉它“我们要用Python编程“:

  1. 打开VS Code

  2. 点击左侧的方块图标(扩展),或者按快捷键 Ctrl + Shift + X

  3. 在搜索框中输入:Python

  4. 找到 “Python” 扩展(作者是 Microsoft),点击 “Install”(安装)按钮

  5. 等待安装完成

👨‍🏫 给家长的Tips

VS Code的扩展(Extension)就像给手机安装APP,可以为软件添加新功能。Python扩展让VS Code能够识别Python代码,提供语法高亮、自动补全、运行按钮等方便功能。

对孩子说: 安装扩展就像给工具箱添加新工具,让我们的编程工作更轻松!

运行第一个程序

环境都准备好了!现在让我们写并运行第一个Python程序。

创建第一个程序文件

  1. 在电脑上创建一个文件夹,比如命名为 python-learn(Python学习)

  2. 进入这个文件夹,在空白处右键点击

  3. 选择 “Open with Code”(用Code打开)

  4. VS Code打开了!在左侧资源管理器中,点击新建文件的图标(或者按 Ctrl + N

  5. Ctrl + S 保存文件,命名为 hello.py

🤔 为什么是 .py?

就像Word文档用 .docx 结尾,照片用 .jpg 结尾一样,Python程序文件使用 .py 作为扩展名。这样电脑就知道这个文件是Python程序。

编写第一个程序

hello.py 文件中输入以下代码:

print("Hello, Python!")

代码说明:

  • print() 是Python的输出命令,可以把内容显示在屏幕上
  • "Hello, Python!" 是字符串,就是一串字符
  • 注意要用英文的双引号 ",不要用中文的引号 "

运行第一个程序

在VS Code中运行程序有好几种方法:

方法一:使用运行按钮(最简单)

  1. 找到代码右上角的 ▶️ 三角形按钮
  2. 点击它,程序就会运行
  3. 在下方的终端窗口中,你应该能看到:
    Hello, Python!
    

方法二:使用快捷键

  1. Ctrl + F5
  2. 选择 “Run Python File in Terminal”
  3. 程序运行,输出结果显示在下方

方法三:在命令行中运行

  1. 在VS Code中,按 Ctrl + `(反引号键)打开终端
  2. 在终端中输入:
    python hello.py
    
  3. 按回车,程序运行

恭喜你!你已经运行了第一个Python程序!🎉

练习环节

练习1:输出不同的内容

修改程序,让它输出你的名字。比如你的名字叫“小明“,程序应该是:

print("小明")

运行看看!

练习2:输出多行内容

一个程序可以有多条输出语句。试试这个:

print("我学习编程")
print("这很有趣")
print("我喜欢Python")

运行程序,看看输出了什么?

练习3:做数学题

Python还能做数学计算!试试:

print(5 + 3)
print(10 - 2)
print(4 * 5)
print(20 / 4)

👨‍🏫 给家长的Tips

这是观察孩子的好机会:

  • 问孩子:“为什么 5 + 3 不用引号,而 小明 要用引号?”
  • 答案:5 + 3 是数学运算,Python会计算结果;而带引号的是字符串,Python会原样输出。
  • 如果孩子不理解,没关系,下一章我们会详细讲解。

熟悉VS Code的基本操作

在进行正式编程前,让我们再熟悉一下VS Code:

新建文件

  • Ctrl + N 新建文件
  • Ctrl + S 保存文件,记得输入 .py 后缀

打开已有文件

  • 在资源管理器中双击文件
  • 或者在文件夹中右键文件,选择“Open with Code“

切换文件

  • 如果打开了多个文件,在顶部的标签栏中点击切换

常用快捷键

  • Ctrl + S:保存(记得经常保存!)
  • Ctrl + Z:撤销(撤销上一步操作)
  • Ctrl + Y:重做(撤销后后悔了,可以重做)
  • Ctrl + F:查找(在文件中搜索内容)

文件管理练习

让我们练习一下文件的基本操作:

  1. 创建一个新文件 practice1.py
  2. 输入一些代码
  3. 保存文件
  4. 运行程序
  5. 关闭文件(点击文件标签的 × 号)
  6. 重新打开 practice1.py
  7. 修改代码
  8. 保存并运行

👨‍🏫 给家长的Tips

培养孩子良好的文件管理习惯:

  • 为不同主题的练习创建不同的文件夹
  • 给文件起有意义的名字(如 rectangle.py 而不是 1.py
  • 经常保存(可以用“每写一行代码就按一次Ctrl+S“来养成习惯)

就像整理书包一样,井井有条的文件管理会让编程更愉快!

本章小结

我们学到了什么?

  1. 编程语言与程序

    • 编程语言是用来和计算机交流的工具
    • 程序是用编程语言写的一系列指令
  2. Python环境安装

    • Python 3.8编程语言
    • VS Code代码编辑器及Python扩展
    • 验证安装是否成功
  3. 编写和运行程序

    • 创建 .py 文件
    • 编写Python代码
    • 在VS Code中运行程序
  4. VS Code基本操作

    • 新建、保存、打开文件
    • 运行程序的几种方法
    • 常用快捷键

下一步预告

你成功运行了第一个程序!下一章,我们将深入学习:

  • 如何在屏幕上显示各种内容
  • 如何让程序接收用户输入
  • 编写更有趣的程序,比如计算长方形面积

家长辅导提示

教学目标

  • 孩子能说出编程语言和程序的区别
  • 孩子能独立运行Python程序
  • 孩子掌握VS Code的基本操作
  • 孩子养成经常保存的习惯
  • 孩子理解计算机的基本组成(五个主要部分)

常见问题解答

Q: 孩子觉得安装步骤太复杂怎么办? A: 可以家长负责主要安装过程,让孩子参与部分步骤,重在体验过程。

Q: VS Code界面是英文的,孩子不认识怎么办? A: 可以下载安装中文版,但是使用英文版本是一个接触和使用英语术语的好机会。常用的几个操作多用几次就记住了(File、Edit、Save等)。也可以打印一份常用操作的中文对照表。

Q: 家长不会编程,能教孩子吗? A: 完全可以!

  • 共学模式:家长先自学,然后教孩子。孩子看到“爸爸妈妈也在学习“会更有激励
  • 陪伴学习:不需要成为编程专家,只需陪伴和引导
  • 解决问题:和孩子一起解决环境配置、安装等技术问题,出问题可以求助 AI
  • 鼓励探索:鼓励孩子自己尝试,犯错也是学习的一部分

Q: 运行程序时出现错误怎么办? A:

  • 检查是否用了中文标点(如中文引号““、中文括号())
  • 检查代码拼写是否正确
  • 查看错误提示,尝试理解

教学方式建议

1. 分阶段进行

  • 第一阶段:环境安装(可以分2-3次完成)
  • 第二阶段:运行第一个程序
  • 第三阶段:熟悉VS Code操作
  • 不要急于求成,每个阶段都充分练习

2. 多用类比和比喻

  • 编程语言 = 人和计算机交流的语言
  • 程序 = 给计算机的详细说明书
  • Python解释器 = 翻译官
  • CPU = 大脑, 内存 = 草稿纸, 硬盘 = 笔记本

3. 鼓励动手尝试

  • 让孩子多修改代码,观察变化
  • “如果不这样写会怎样?”——鼓励实验
  • 即使出错也是宝贵的学习经验

4. 联系生活实际

  • 计算机的输入输出 = 人的眼睛耳朵和嘴巴
  • 程序执行 = 按菜谱做菜的步骤
  • 变量 = 盒子,函数 = 快捷指令

5. 保持兴趣第一

  • 不要纠结语法细节
  • 多鼓励和表扬
  • 让孩子看到立即可见的成果(程序运行成功)

评估与反馈

完成本章学习后,观察孩子:

  • 对编程是否有兴趣?
  • 遇到错误时的反应(沮丧还是好奇)?
  • 是否愿意尝试修改代码?
  • 能否理解计算机的基本组成?

根据孩子的反应调整后续教学节奏。记住:兴趣第一,进度第二!

知识补充:关于计算机的一些常识

Q: CPU的主频是什么意思? A: CPU的主频(比如3.0GHz)表示CPU每秒可以执行的周期数。简单理解:

  • 1GHz = 每秒10亿次周期
  • 3.0GHz = 每秒30亿次周期
  • 这不是直接等于每秒执行30亿条指令(因为一条指令可能需要多个周期)
  • 但主频越高,CPU通常越快

Q: 内存和硬盘有什么区别? A:

  • 内存
    • 速度快,但容量小(如8GB、16GB)
    • 断电后数据丢失
    • 存储正在运行的程序和数据
  • 硬盘
    • 速度相对慢,但容量大(如512GB、1TB)
    • 断电后数据仍保存
    • 长期存储文件和程序

Q: 为什么程序需要调入内存才能运行? A:

  • 硬盘的读取速度太慢,如果直接从硬盘运行程序会非常卡
  • 内存的速度快,适合CPU快速读取指令
  • 类比:你要做作业时,会把书从书架(硬盘)拿到桌上(内存),因为桌面上拿取更方便

准备好继续探索了吗?让我们进入第2章,开始真正的编程吧! 🎯

输入与输出

引言

计算机程序就像一个神奇的魔法盒子,它可以接收你的输入(Input),经过处理后,再给你输出(Output)。这一章,我们要学习如何让计算机程序和你“对话“——接收你的指令和信息,然后把结果显示给你。

计算机的输入输出设备 🖥️

在我们学习编程之前,先来认识一下计算机的“五官“——输入输出设备:

输入设备:计算机用来接收信息的工具

  • 🎹 键盘:像计算机的“耳朵“,接收你按下的按键
  • 🖱️ 鼠标:像计算机的“手“,接收你的点击和移动
  • 🎤 麦克风:像计算机的“听觉器官“,接收你的声音

输出设备:计算机用来展示信息的工具

  • 📺 显示器:像计算机的“嘴巴“,把结果展示给你看
  • 🔊 扬声器:像计算机的“声音“,播放音乐和音效
  • 🖨️ 打印机:把计算机里的内容打印在纸上

想象一下,你在玩一个电子游戏:

  • 输入:你按下键盘上的按钮、点击鼠标 → 输入设备在工作
  • 处理:计算机的CPU(中央处理器)计算游戏画面
  • 输出:屏幕上显示游戏画面、播放音乐、显示分数 → 输出设备在工作

我们写程序时,最常用的输入输出方式就是:

  • 输入:通过键盘输入文字或数字
  • 输出:在屏幕上显示文字或数字

🎯 本章目标

通过这一章,你将学会:

  • 让计算机“说话“(输出)
  • 让计算机“倾听“(输入)
  • 用变量存储数据
  • 做一个能做数学题的计算器!

输出

认识 print 函数

在Python中,让程序“说话“的方法叫做 print(打印)。print 可以把内容显示在屏幕上。

让我们试试看:

print(5)
print("Jerry")  # 双引号中的是字符串,基本按照原样输出
print("Jerry, ", 10, " years old")  # 多个输出,用逗号隔开
print() # 输出空行
print(5 + 3) # 输出计算结果

运行结果:

5
Jerry
Jerry,  10  years old

8

👨‍🏫 给家长的小贴士

  • 这里的 print 是一个“函数“(function),现在孩子不用理解什么是函数,只需要知道它是一个工具,可以帮我们输出内容
  • 双引号 "" 中的内容叫做“字符串“(string),会原样输出
  • 如果没有双引号,Python会把它当作数字或变量来处理
  • # 后面的是注释,计算机不会执行,只是给人看的说明
  • 教学建议:可以让孩子先运行这段代码,然后修改数值再次运行,观察输出结果的变化,这样可以让孩子理解“输出“的概念

实践:打印长方形信息

现在我们用 print 来展示一个长方形的信息:

print("rectangle")
print("---------------")
print("length   : ", 5)
print("width    : ", 4)
print("area     : ", 5 * 4)
print("perimeter : ", (5 + 4) * 2)

运行结果:

rectangle
---------------
length   :  5
width    :  4
area     :  20
perimeter :  18

📐 数学小知识

  • 面积 = 长 × 宽
  • 周长 = (长 + 宽) × 2
  • 这个例子中:面积 = 5 × 4 = 20,周长 = (5 + 4) × 2 = 18

想一想:如果要计算宽为3的长方形,如何修改程序?

答案:你需要把所有 4 改成 3

print("rectangle")
print("---------------")
print("length   : ", 5)
print("width    : ", 3)  # 改这里
print("area     : ", 5 * 3)  # 改这里
print("perimeter : ", (5 + 3) * 2)  # 改这里

但是这样改来改去很麻烦,有没有更好的办法呢?

使用变量

什么是变量?

变量就像是贴了标签的盒子。

想象你的书桌上有很多盒子:

  • 每个盒子上都贴着一个标签(名字)
  • 你可以在盒子里放东西(值)
  • 你可以随时打开盒子,看看里面是什么
  • 你也可以随时换掉盒子里的东西

在Python中,我们用 = 来给变量“赋值“(给盒子装东西):

  • = 的左边是盒子的标签(变量名)
  • = 的右边是要放进去的东西(值)
l = 5  # 把数字5放进标签为"l"的盒子
w = 4  # 把数字4放进标签为"w"的盒子

用变量改进长方形程序

现在我们用变量来重写长方形程序:

l = 5  # length(长度)的缩写
w = 4  # width(宽度)的缩写

print("rectangle")
print("---------------")
print("length   : ", l)
print("width    : ", w)
print("area     : ", l * w)
print("perimeter : ", (l + w) * 2)

现在,如果你想计算宽为3的长方形,只需要改一个地方:

l = 5
w = 3  # 只需要改这里!

print("rectangle")
print("---------------")
print("length   : ", l)
print("width    : ", w)
print("area     : ", l * w)
print("perimeter : ", (l + w) * 2)

变量的命名规则

给变量起名字时,要遵守一些规则:

✅ 可以用:

  • 英文字母(大小写都可以)
  • 数字(但不能作为开头)
  • 下划线 _

❌ 不能用:

  • 数字开头(如 1name
  • 空格或特殊符号(如 my-namename@
  • Python的关键字(如 printinput 等)

好的变量名例子:

length = 5  # 用完整的英文单词
width = 4
l = 5  # 用有意义的缩写
w = 4
my_name = "Tom"  # 多个单词用下划线连接

不好的变量名例子:

a = 5  # 看不出是什么意思
x1 = 5  # 没有意义

更改变量的值

变量盒子里面的东西可以随时更换:

w = 4
print(w)  # 输出:4

w = 6
print(w)  # 输出:6

w = w + 1
print(w)  # 输出:7(解释:把w的值加1后重新放回w里)

👨‍🏫 给家长的小贴士

  • 孩子可能会对 w = w + 1 感到困惑,因为在数学中这是不成立的
  • 在编程中,= 是“赋值“而不是“相等“
  • 可以这样解释:把盒子w里的东西拿出来,加1后再放回去
  • 教学方式:用实物演示,比如准备一些小积木放在盒子里,让孩子亲手操作,理解“取出、加1、放回“的过程

练习1:正方形计算器

任务:参照上面的长方形程序,写一个计算正方形周长和面积的程序。

对于一个边长为3的正方形,要能输出如下:

square
----------------
side : 3
area : 9
perimeter : 12

提示

  • 正方形只需要一个变量 side(边长)
  • 📐 数学知识
    • 正方形的四条边都相等
    • 面积 = 边长 × 边长
    • 周长 = 边长 × 4
  • 这个例子中:面积 = 3 × 3 = 9,周长 = 3 × 4 = 12
点击查看答案
side = 3

print("square")
print("---------------")
print("side : ", side)
print("area : ", side * side)
print("perimeter : ", side * 4)

输入

什么是输入?

之前我们学习了“输出“,程序可以把结果显示在屏幕上。但只有输出还不够,我们希望程序能和用户“对话“。

这就需要用到输入功能!

🔄 输入输出的循环

  1. 输出:程序在屏幕上显示问题(通过显示器这个输出设备)
  2. 用户思考:你看到问题,在脑子里想答案
  3. 输入:你通过键盘输入答案(通过键盘这个输入设备)
  4. 处理:程序接收你的答案,进行处理
  5. 输出:程序再次显示结果(通过显示器)

这个循环就像两个人在对话:

  • 程序“说话“(输出)
  • 你“说话“(输入)
  • 程序理解并“回答“(处理和输出)

认识 input 函数

Python中的 input() 函数可以让程序接收你通过键盘输入的信息。

让我们一步步学习:

最简单的输入

name = input()
print("Hi, ", name)

运行这段程序时:

  1. 程序会停下来等待(你看不到任何提示)
  2. 你输入名字(比如“Tom“)并按回车
  3. 程序继续执行,输出 Hi, Tom

添加提示信息

print("What is your name?")
name = input()
print("Hi, ", name)

这样用户就知道该做什么了!

更简洁的写法

我们可以把提示信息直接放在 input() 的括号里:

name = input("What is your name?")
print("Hi, ", name)

👨‍🏫 给家长的小贴士

  • 括号里的文字叫做“参数“(parameter),现在孩子不需要理解这个词
  • 可以这样解释:把提示信息“交给“ input 函数,让它在等待输入时显示出来
  • 教学建议:可以用角色扮演的方式,家长扮演“程序“,孩子扮演“用户“,模拟输入输出的对话过程,帮助孩子理解程序与人的交互

让输出更美观

我们可以在问候语前加一个空行:

name = input("What is your name?")
print()  # 输出一个空行
print("Hi, ", name)

或者在提示语后加换行符 \n

name = input("What is your name?\n")  # \n 表示换行
print()
print("Hi, ", name)

关于 \n

  • \n 是一个特殊符号,表示“换行“(new line)
  • \ 叫做“转义符“,它让后面的 n 不再是字母n,而是代表换行

输入多个信息

现在我们让程序更聪明一点,询问用户的更多信息:

name = input("What is your name?")
age = input("How old are you?")

print("Haha, you are ", name)

运行示例:

What is your name? Tom
How old are you? 10
Haha, you are  Tom

练习2:个人信息汇总

任务:写一个程序,询问用户的名字和年龄,然后输出一句话介绍他/她。

期望输出:

What is your name? Tom
How old are you? 10

Haha, you are Tom, you are 10 years old
点击查看答案
name = input("What is your name?")
age = input("How old are you?")

print()
print("Haha, you are ", name, ", you are ", age, " years old")

更漂亮的输出方式

上面的输出会有很多空格,因为 print() 默认会在逗号 , 的位置加空格。Python 3.6+ 提供了一种更漂亮的输出方式,叫做 f-string(格式化字符串):

name = input("What is your name?")
age = input("How old are you?")

print(f"Haha, you are {name}, you are {age} years old")

注意

  • print() 前面的字母 f 不能忘记!
  • {name}{age} 会自动替换成变量的值
  • 这种方式更灵活,你可以自由决定在哪里加空格

给家长的小贴士

  • f-string 是 Python 3.6 引入的新特性,现代Python代码推荐使用
  • 可以这样解释:字母 f 告诉Python“这里有变量要替换“
  • {} 就像是一个小窗口,透过它可以看到变量盒子里的内容

综合练习:交互式计算器

现在让我们把输入和输出结合起来,做一个真正的交互式程序!

练习3:长方形计算器(最终版)

任务:写一个程序,询问用户长方形的长和宽,然后输出面积和周长。

期望输出示例:

rectangle
---------------
length ? 5
width ? 4
area : 20
perimeter : 18
点击查看答案
print("rectangle")
print("---------------")

l = int(input("length ? "))
w = int(input("width ? "))

print("area : ", l * w)
print("perimeter : ", (l + w) * 2)

说明

  • int(input(...)) 先获取输入,再立即转换成数字
  • 这样 lw 就是数字类型,可以直接进行数学运算
  • 关于 int() 的详细讲解在下一章

👨‍🏫 给家长的小贴士

  • 这里出现了一个新问题:input() 返回的是“字符串“,不能直接做数学运算
  • int() 函数可以把字符串转换成数字(integer,整数)
  • 这个知识点在第三章会详细讲解
  • 如果孩子提出疑问,可以先简单解释:计算机把输入当作文字,需要告诉它“这是数字“
  • 教学建议:可以演示一下不加 int() 会发生什么错误,让孩子理解数据类型的重要性

综合练习:数学应用题 📝

现在让我们用输入输出功能来解决一些数学应用题!

练习4:年龄计算器

任务:写一个程序,询问用户今年的年龄,然后计算5年后的年龄。

期望输出示例:

How old are you? 10

Five years later, you will be 15 years old
点击查看答案
age = int(input("How old are you? "))

print()
print(f"Five years later, you will be {age + 5} years old")

📐 数学知识

  • 这是简单的加法运算:未来年龄 = 现在年龄 + 年数
  • 本题中:15 = 10 + 5

练习5:价格计算器

任务:写一个程序,询问用户购买苹果的数量和单价,计算总价。

期望输出示例:

How many apples? 6
Price per apple? 2

Total price: 12 yuan
点击查看答案
count = int(input("How many apples? "))
price = int(input("Price per apple? "))

print()
print(f"Total price: {count * price} yuan")

📐 数学知识

  • 总价 = 单价 × 数量
  • 本题中:12 = 2 × 6

练习6:速度计算器

任务:写一个程序,询问用户汽车的速度和行驶时间,计算行驶距离。

期望输出示例:

Speed (km/h)? 60
Time (hours)? 2

Distance: 120 km
点击查看答案
speed = int(input("Speed (km/h)? "))
time = int(input("Time (hours)? "))

print()
print(f"Distance: {speed * time} km")

📐 数学知识

  • 距离 = 速度 × 时间
  • 本题中:120 = 60 × 2

👨‍🏫 给家长的小贴士

  • 这些练习都是小学数学常见的应用题类型
  • 通过编程练习,可以加深孩子对数学公式的理解
  • 教学建议
    • 先让孩子用纸笔算出答案,再编程验证
    • 鼓励孩子自己出题,交换做题
    • 可以讨论生活中还有哪些类似的计算问题

本章小结

恭喜你完成了这一章!你已经学会了:

  1. 输出:使用 print() 在屏幕上显示内容
  2. 变量:用标签盒子存储和改变数据
  3. 输入:使用 input() 获取用户输入
  4. f-string:用 {} 和字母 f 美化输出
  5. 数学应用:用编程解决年龄、价格、距离等数学问题

重要概念

  • ✅ 变量就像贴了标签的盒子
  • = 是“赋值“,不是“相等“
  • input() 得到的是字符串
  • print() 可以输出多个内容,用逗号隔开
  • ✅ 输入输出设备(键盘、显示器)是计算机与用户交互的“五官“

🖥️ 计算机知识回顾

  • 输入设备:键盘、鼠标、麦克风等,让计算机接收信息
  • 输出设备:显示器、扬声器、打印机等,让计算机展示信息
  • 程序交互:通过输入输出,程序可以和人“对话“
  • 数据处理:输入 → 处理 → 输出,这是计算机工作的基本流程

📐 数学知识回顾

  • 长方形:面积 = 长 × 宽,周长 = (长 + 宽) × 2
  • 正方形:面积 = 边长 × 边长,周长 = 边长 × 4
  • 加法:未来年龄 = 现在年龄 + 年数
  • 乘法:总价 = 单价 × 数量,距离 = 速度 × 时间

🎉 下节预告:下一章我们会详细学习“字符串“——文字在计算机中是如何存储和处理的。你会发现,文字也可以做很多有趣的操作!


💡 给家长的建议

  • 这一章是编程基础,建议让孩子多练习,熟练掌握输入输出
  • 可以让孩子设计自己的“小问题“,用编程来解答
  • 鼓励孩子用数学知识来解决实际问题,增强学习兴趣
  • 如果孩子对某个概念不理解,可以结合生活中的例子来解释

字符串变量与操作

引言

在上一章中,我们学习了如何让程序“说话“(输出)和“倾听“(输入)。你可能已经注意到,input() 函数接收到的内容,以及 print() 函数输出的那些文字,都叫做“字符串“(string)。

字符串就是计算机处理文字信息的方式。它就像一串珍珠项链,每个字符(字母、数字、符号、汉字)都是一颗珍珠,按顺序串在一起。

💾 计算机如何存储文字?

你可能好奇,计算机怎么“记住“这些文字呢?

🖥️ 计算机的小知识

  • 计算机的内存(RAM)就像一个巨大的柜子,里面有很多小格子
  • 每个格子里可以存放一个字符
  • 字符串就是一连串的格子,每个格子里放一个字符
  • 比如 "Hello" 就需要5个格子,分别存放 H、e、l、l、o
内存中的样子:
┌───┬───┬───┬───┬───┐
│ H │ e │ l │ l │ o │
└───┴───┴───┴───┴───┘

📏 内存占用

  • 每个字符在内存中占用一定的空间
  • 在Python中,一个英文字符通常占用1-2个字节
  • 一个中文字符通常占用2-4个字节
  • 所以,字符串越长,占用的内存空间越大

这一章,我们要深入了解:

  • 什么是字符串变量
  • 如何创建和使用字符串变量
  • 字符串的基本操作:拼接、重复
  • 如何让字符串和数字一起工作
  • 📐 字符串位置与数轴的关系

回顾:变量是什么

在上一章中,我们已经学过变量的概念。还记得我们用的“盒子“类比吗?

变量就像一个盒子

  • 盒子有一个名字(变量名)
  • 盒子里可以装一个东西(变量的值)
  • 我们可以随时把盒子里的东西拿出来或换成别的东西
name = "小明"  # 创建一个叫name的盒子,里面装了"小明"
age = "10"     # 创建一个叫age的盒子,里面装了"10"

👨‍🏫 给家长的小贴士

  • 变量名要使用有意义的英文名字,比如用 name 而不是 n
  • 变量名区分大小写,nameName 是两个不同的变量
  • 变量名不能用数字开头,也不能包含空格
  • 变量名最好用小写字母,多个单词用下划线连接,如 first_name
  • 教学建议
    • 变量名就像盒子的标签,要清晰说明盒子里装的是什么
    • 可以用实物(盒子、标签)演示变量的概念
    • 数据存储的抽象性:孩子可能对“看不见的存储“感到困惑,可以解释为:
      • 变量就像在纸上写下的名字,我们可以随时查看这个名字代表什么
      • 计算机内存就是计算机的“草稿纸“,用来暂时存放信息

字符串变量的创建

在Python中,创建字符串变量非常简单,只需要用引号把文字括起来。

引号的用法

Python支持三种引号:

  • 双引号 "":最常用
  • 单引号 '':和双引号一样,看个人喜好
  • 三引号 """''':用于多行文字
# 使用双引号
s1 = "hello"
print(s1)  # 输出:hello

# 使用单引号
s2 = 'world'
print(s2)  # 输出:world

# 如果文字中本身就包含引号,可以使用另一种引号
s3 = "He said: 'Hello!'"
print(s3)  # 输出:He said: 'Hello!'

s4 = 'It\'s a nice day'  # 使用反斜杠转义
print(s4)  # 输出:It's a nice day

👨‍🏫 给家长的小贴士

  • 建议让孩子主要使用双引号,这样更统一
  • 如果字符串中包含双引号,就用单引号包裹
  • \ 是“转义字符“,告诉Python后面的引号是文字的一部分,不是字符串的结束
  • 暂时不用深入讲解转义字符的概念,孩子遇到时再解释即可
  • 计算机编码知识(补充):
    • 计算机不认识“字母“或“汉字“,只认识数字
    • 所以需要把每个字符对应到一个数字,这就是“编码“
    • ASCII码:英文字符的编码,比如A=65,B=66,a=97
    • Unicode:统一的编码标准,可以表示所有语言的字符
    • 现在Python默认使用Unicode,可以处理中文、日文、韩文等各种语言
    • 这些知识不用深入讲,只是让孩子知道:计算机用数字来存储文字

多行字符串

有时候我们需要写很长、很多行的文字,比如一首诗或一段对话,这时可以用三引号:

poem = """静夜思
床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。"""

print(poem)

运行结果:

静夜思
床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。

字符串的基本操作

1. 字符串拼接(+)

就像把两段绳子接在一起,我们可以用 + 号把两个字符串连接成一个新的字符串。

first_name = "张"
last_name = "三"
full_name = first_name + last_name

print(full_name)  # 输出:张三

# 也可以直接拼接文字
greeting = "Hello, " + "World!"
print(greeting)  # 输出:Hello, World!

注意:拼接时不会自动添加空格,需要自己加上:

s1 = "Hello"
s2 = "World"

# 错误:没有空格
print(s1 + s2)  # 输出:HelloWorld

# 正确:手动添加空格
print(s1 + " " + s2)  # 输出:Hello World

2. 字符串重复(*)

如果你想打印一条分隔线,比如 10 个 -,不需要一个一个敲出来,可以用 * 号让字符串“复制“多次:

line = "-" * 10
print(line)  # 输出:----------

# 也可以用于其他文字
laugh = "哈" * 5
print(laugh)  # 输出:哈哈哈哈哈

# 组合使用
border = "=" * 20
print(border)
print("  我的通知  ")
print(border)

运行结果:

====================
  我的通知
====================

3. 获取字符串长度

有时候我们需要知道一个字符串有多少个字符,可以使用 len() 函数:

name = "张三"
print(len(name))  # 输出:2

sentence = "Hello World"
print(len(sentence))  # 输出:11(包含空格)

empty = ""
print(len(empty))  # 输出:0

给家长的小贴士

  • len() 是 length(长度)的缩写
  • 空格也是字符,会被计入长度
  • 每个汉字算 1 个字符(在 Python 中)
  • 空字符串 "" 的长度是 0

实践 1:制作名片

让我们用字符串拼接来制作一张简单的名片:

# 名片程序
print("=" * 30)
name = "张小明"
age = "10岁"
school = "阳光小学"
hobby = "画画、读书"

print("姓名:" + name)
print("年龄:" + age)
print("学校:" + school)
print("爱好:" + hobby)
print("=" * 30)

运行结果:

==============================
姓名:张小明
年龄:10岁
学校:阳光小学
爱好:画画、读书
==============================

练习 1:自我介绍

请编写一个程序,输出你自己的自我介绍。要求:

  1. 使用变量存储你的姓名、年龄、喜欢的颜色、喜欢的食物
  2. 使用字符串拼接把信息组合起来
  3. 使用 * 号制作上下边框
  4. 使用 len() 函数显示你名字的字数
📝 点击查看参考答案
# 自我介绍程序
print("=" * 25)

# 存储个人信息
my_name = "李小花"
my_age = "9岁"
my_color = "粉色"
my_food = "冰淇淋"

# 输出信息
print("我是:" + my_name)
print("今年:" + my_age)
print("喜欢的颜色:" + my_color)
print("喜欢的食物:" + my_food)
print("我的名字有" + str(len(my_name)) + "个字")

print("=" * 25)

运行结果:

=========================
我是:李小花
今年:9岁
喜欢的颜色:粉色
喜欢的食物:冰淇淋
我的名字有3个字
=========================

给家长的小贴士

  • 如果孩子在 len() 的输出拼接时遇到困难,可以提示他们用 str() 把数字转换成字符串
  • 鼓励孩子尝试改变边框的样式和长度

字符串的索引和切片

字符串中的每个字符都有一个位置编号,这个编号叫做“索引“(index)。我们可以通过索引来获取字符串中的某个字符或某一部分。

📐 索引与数轴

在数学课上,你学过数轴吗?数轴是一条直线,上面有很多刻度,每个刻度对应一个数字:

数轴:
    0    1    2    3    4    5
    │    │    │    │    │    │
────┼────┼────┼────┼────┼────
    │    │    │    │    │    │

字符串的索引就像数轴:

字符串:  H     e     l     l     o     ,
索引:    0     1     2     3     4     5
          ↓     ↓     ↓     ↓     ↓     ↓
  • 索引 0 对应第一个字符 H (就像数轴上的 0)
  • 索引 1 对应第二个字符 e (就像数轴上的 1)
  • 以此类推…

📐 数学联系:

  • 数轴上的每个点对应一个数字
  • 字符串中的每个位置对应一个索引
  • 它们都是用数字来标识“位置“的方法

索引从 0 开始

在Python中,索引从 0 开始计数,而不是 1。这对初学者来说需要适应一下:

字符串:  H  e  l  l  o  ,  W  o  r  l  d  !
索引:    0  1  2  3  4  5  6  7  8  9  10 11

为什么要从0开始?

  • 这与计算机的底层工作方式有关
  • 可以理解为“偏移量“:要找第1个字符,需要偏移0个位置;要找第2个字符,需要偏移1个位置
  • 就像在数轴上,0是起始点,不是第1个点
s = "Hello, World!"

# 获取单个字符
print(s[0])   # 输出:H(第1个字符)
print(s[4])   # 输出:o(第5个字符)
print(s[7])   # 输出:W(第8个字符)

负数索引

Python还支持从右边数,使用负数索引:

字符串:  H  e  l  l  o  ,  W  o  r  l  d  !
负索引:  -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1
s = "Hello, World!"

print(s[-1])   # 输出:!(倒数第1个)
print(s[-6])   # 输出:W(倒数第6个)

👨‍🏫 给家长的小贴士

  • 可以用“从0开始“和“从-1开始倒着数“来记忆
  • 索引 0 是第一个字符,索引 -1 是最后一个字符
  • 可以用手指着每个字符数给孩子看,帮助他们理解
  • 数学类比
    • 可以用数轴来帮助孩子理解索引的概念
    • 强调索引就像数轴上的刻度,从0开始标记位置
    • 负数索引就像从数轴的右边往左数

字符串切片

切片(slice)可以获取字符串的一部分。语法是:字符串[开始位置:结束位置]

s = "Hello, World!"

# 获取子串(注意:不包含结束位置)
print(s[0:5])    # 输出:Hello(从位置0到4)
print(s[7:12])   # 输出:World(从位置7到11)

# 省略开始或结束位置
print(s[:5])     # 输出:Hello(从开头到位置4)
print(s[7:])     # 输出:World!(从位置7到结尾)
print(s[:])      # 输出:Hello, World!(复制整个字符串)

# 使用负数索引
print(s[-6:])    # 输出:World!(倒数第6个到结尾)
print(s[:-7])    # 输出:Hello(开头到倒数第7个)

重要提示:切片的“结束位置“是不包含的!s[0:5] 只包含索引 0,1,2,3,4,不包含 5。

给家长的小贴士

  • 切片的概念对孩子来说可能比较抽象,可以用“切蛋糕“来类比
  • 强调“结束位置不包含“这个规则
  • 建议先掌握基本用法 s[开始:结束],负数索引可以以后慢慢理解
  • 如果孩子感到困惑,不要强求,以后用到时再巩固

练习 2:名字的秘密

编写一个程序,分析一个名字:

  1. 获取名字的第一个字
  2. 获取名字的最后一个字
  3. 如果名字是两个字,分别输出两个字
  4. 如果名字是三个字,输出中间的字
  5. 使用 len() 判断名字有几个字
📝 点击查看参考答案
# 名字分析程序
name = "张小明"

# 输出名字长度
length = len(name)
print("名字有" + str(length) + "个字")

# 获取第一个字
first = name[0]
print("第一个字:" + first)

# 获取最后一个字
last = name[-1]
print("最后一个字:" + last)

# 根据长度做不同处理
if length == 2:
    print("姓:" + name[0])
    print("名:" + name[1])
elif length == 3:
    print("姓:" + name[0])
    print("名:" + name[1:3])

给家长的小贴士

  • 这个练习需要用到第5章的条件语句,如果孩子还没学,可以先让他们处理固定长度的名字
  • 可以让孩子先试试处理自己的名字,然后试试同学的不同长度的名字
  • 这个练习展示了字符串操作的实际应用

字符串和数字的转换

还记得我们在上一章遇到过一个问题吗?当我们用 input() 获取数字时,程序计算长方形面积出错了:

l = input("length ?\n")  # 输入:5
w = input("weight ?\n")  # 输入:3
print(l * w)  # 错误!输出:333(而不是15)

为什么会这样?因为 input() 得到的是字符串,不是数字!字符串的 * 操作是重复,不是乘法:

s = "3"
print(s * 5)  # 输出:33333(重复5次)

字符串转数字:int()

要让字符串“变成“数字,我们需要使用 int() 函数:

s = "5"
n = int(s)    # 把字符串"5"转换成数字5
print(n * 3)  # 输出:15(这是数学乘法)

# 在输入时直接转换
l = input("length ?\n")
w = input("weight ?\n")

# 转换成数字
l = int(l)
w = int(w)

# 现在可以正常计算了
print("面积:", l * w)  # 输出:面积:15

数字转字符串:str()

有时候我们也需要把数字转换成字符串,比如拼接文字:

age = 10  # 这是一个数字
message = "我今年" + age + "岁"  # 错误!不能直接拼接

# 需要转换
age = 10
message = "我今年" + str(age) + "岁"  # 正确
print(message)  # 输出:我今年10岁

给家长的小贴士

  • int 是 integer(整数)的缩写
  • str 是 string(字符串)的缩写
  • 强调:字符串的 * 是重复,数字的 * 是乘法
  • 这个转换过程叫做“类型转换“(type conversion)
  • 可以用“翻译“来类比:就像把中文翻译成英文,我们把“字符串“翻译成“数字“

实践 2:改进的长方形计算器

现在我们可以修复上一章的计算器程序了:

# 长方形计算器(改进版)
print("=" * 30)
print("  长方形计算器  ")
print("=" * 30)

# 获取输入
l = input("请输入长度(数字):")
w = input("请输入宽度(数字):")

# 转换成数字
l = int(l)
w = int(w)

# 计算
area = l * w
perimeter = (l + w) * 2

# 输出结果
print()
print("计算结果:")
print("-" * 20)
print("长度:" + str(l))
print("宽度:" + str(w))
print("面积:" + str(area))
print("周长:" + str(perimeter))
print("-" * 20)

运行示例:

==============================
  长方形计算器
==============================
请输入长度(数字):5
请输入宽度(数字):3

计算结果:
--------------------
长度:5
宽度:3
面积:15
周长:16
--------------------

练习 3:正方形计算器

编写一个完整的正方形计算器程序:

  1. 询问用户正方形的边长
  2. 计算正方形的面积和周长
  3. 输出格式化结果(使用拼接和重复)
  4. 添加分隔线和标题
📝 点击查看参考答案
# 正方形计算器
print("=" * 25)
print("  正方形计算器  ")
print("=" * 25)
print()

# 获取边长并转换
side = input("请输入正方形的边长:")
side = int(side)

# 计算
area = side * side
perimeter = side * 4

# 输出
print()
print("✿ 计算结果 ✿")
print("-" * 20)
print("边长:" + str(side))
print("面积:" + str(area))
print("周长:" + str(perimeter))
print("-" * 20)
print()
print("小提示:")
print("  面积 = 边长 × 边长")
print("  周长 = 边长 × 4")

字符串的常用方法

在Python中,字符串有很多内置的“工具“,叫做“方法“(method)。这些方法可以让字符串做各种有趣的事情。

大小写转换

s = "Hello World"

# 全部大写
print(s.upper())  # 输出:HELLO WORLD

# 全部小写
print(s.lower())  # 输出:hello world

# 首字母大写
print(s.title())  # 输出:Hello World

去除空格

s = "  hello  "

# 去除两边空格
print(s.strip())       # 输出:hello

# 去除左边空格
print(s.lstrip())      # 输出:hello

# 去除右边空格
print(s.rstrip())      # 输出:  hello

查找和替换

s = "Hello World"

# 查找子串位置
print(s.find("World"))  # 输出:6
print(s.find("Python")) # 输出:-1(找不到)

# 替换
print(s.replace("World", "Python"))  # 输出:Hello Python

判断

# 判断开头
s = "Hello World"
print(s.startswith("Hello"))  # 输出:True
print(s.startswith("World"))   # 输出:False

# 判断结尾
print(s.endswith("World"))     # 输出:True

# 判断全是数字
print("123".isdigit())        # 输出:True
print("abc".isdigit())        # 输出:False

# 判断全是字母
print("abc".isalpha())        # 输出:True
print("abc123".isalpha())     # 输出:False

给家长的小贴士

  • 这些方法的语法是 字符串.方法名(),可以理解为“让字符串做什么事“
  • 方法名后面的括号 () 是必须的,即使里面什么都不放
  • 暂时不需要深入理解“方法“的概念,孩子只要会用即可
  • 第5章我们会学到,TrueFalse 是布尔值,用于判断

综合练习:密码检查器

让我们做一个简单的密码检查器:

  1. 密码不能少于6个字符
  2. 密码必须包含字母和数字
  3. 去除密码前后的空格
# 密码检查器
print("🔐 密码检查器 🔐")
print("=" * 25)
print()

# 获取密码
password = input("请输入密码:")

# 去除空格
password = password.strip()

# 检查长度
if len(password) < 6:
    print("❌ 密码太短了,至少需要6个字符")
else:
    # 检查是否包含数字
    has_digit = False
    for char in password:
        if char.isdigit():
            has_digit = True
            break

    # 检查是否包含字母
    has_alpha = False
    for char in password:
        if char.isalpha():
            has_alpha = True
            break

    if has_digit and has_alpha:
        print("✅ 密码符合要求!")
    else:
        print("❌ 密码必须包含字母和数字")
        if not has_digit:
            print("   缺少数字")
        if not has_alpha:
            print("   缺少字母")

给家长的小贴士

  • 这个练习用到了第5章的条件语句和第6章的循环语句
  • 如果孩子还没学到,可以先做简化版:只检查长度
  • 这是一个实际应用的例子,让孩子看到编程的实用性

字符串的格式化(补充)

在第2章中,我们学习了用 + 拼接字符串。Python还有更强大的格式化方法。

f-string(推荐)

f-string 是Python 3.6引入的新特性,非常方便:

name = "小明"
age = 10

# 使用 f-string
message = f"我叫{name},今年{age}岁"
print(message)  # 输出:我叫小明,今年10岁

# 可以进行计算
side = 5
area = side * side
print(f"边长是{side}的正方形,面积是{area}")

给家长的小贴士

  • f-string 的 f 表示 format(格式化)
  • 花括号 {} 中的变量会被替换成它的值
  • 这比用 +str() 方便很多
  • 建议让孩子使用 f-string,这是现代Python的推荐方式

章节小结

恭喜你完成了这一章!让我们回顾一下学到的内容:

核心概念

  1. 字符串:用引号括起来的一串字符
  2. 字符串变量:存储字符串的变量
  3. 索引:字符在字符串中的位置(从0开始)
  4. 切片:获取字符串的一部分
  5. 类型转换int() 转成数字,str() 转成字符串

常用操作

  • 拼接+ 把两个字符串连起来
  • 重复* 让字符串重复多次
  • 长度len() 获取字符个数
  • 索引s[0] 获取第1个字符,s[-1] 获取最后1个字符
  • 切片s[0:5] 获取从位置0到4的子串

常用方法

  • s.upper() / s.lower():大小写转换
  • s.strip():去除空格
  • s.find():查找子串
  • s.replace():替换
  • s.isdigit() / s.isalpha():判断

重要提示

  • ✅ 字符串的 * 是重复,数字的 * 是乘法
  • input() 得到的是字符串,需要 int() 转换才能计算
  • ✅ 索引从 0 开始,切片不包括结束位置
  • ✅ 字符串在内存中占用空间,越长占用越多

🖥️ 计算机知识回顾

  • 内存存储:字符串存在内存的格子中,每个字符占一个格子
  • 字符编码:计算机用数字来存储文字(ASCII、Unicode)
  • 内存占用:字符越长,占用的内存空间越大
  • 数据抽象:变量是数据的“标签“,让我们方便地访问和操作数据

📐 数学知识回顾

  • 数轴类比:字符串的索引就像数轴上的刻度
  • 位置标记:从0开始标记位置,是数学和编程中常用的方法
  • 负数概念:负数索引就像从数轴右边往左数

给家长的小贴士:教学建议

教学重点

  1. 变量概念:用“盒子“类比,多举生活中的例子
  2. 类型转换:这是最容易出错的地方,要强调字符串和数字的区别
  3. 索引从0开始:这对孩子来说很反直觉,需要多练习
  4. 切片不包括结束位置:同样需要反复强调

常见问题及解答

  1. 问:为什么索引要从0开始?

    • 答:这是计算机科学的传统,可以解释为“偏移量“——第1个字符的偏移量是0。如果孩子不理解,就说“这是规定,记住就好“。
  2. 问:什么时候用单引号,什么时候用双引号?

    • 答:都可以,但要保持一致。建议主要用双引号,除非文字中包含双引号。
  3. 问:为什么 int("3.5") 会出错?

    • 答:int() 只能转换整数,"3.5" 是小数。这是进阶内容,现在先记住 int() 转换整数即可。
  4. 问:len() 计算的是字数还是字符数?

    • 答:是字符数。在Python中,每个汉字、字母、符号、空格都是1个字符。

练习建议

  • 让孩子用字符串操作制作各种“小工具“:名片生成器、密码检查器、名字分析器
  • 多用生活中的例子:名字、地址、课程表
  • 鼓励孩子尝试不同的组合,发现新玩法

挑战练习

  1. 回文检查器:编写程序检查一个词或句子是否是“回文“(正读反读都一样,如“上海自来水来自海上“)

  2. 名字生成器:输入姓氏和名字,生成3个不同的昵称

  3. 文字加密器:把每个字母替换成字母表中下一个字母(a→b, b→c, …, z→a)

  4. 格式化输出:把一个数字(如123456789)格式化成货币格式(如“123,456,789“)

🔗 下一章:数值变量与操作

在下一章中,我们将学习:

  • 整数和小数的区别
  • 数值的运算(加减乘除、求余数、幂运算)
  • 数学函数的使用
  • 数值的格式化输出

字符串帮我们处理文字,数值帮我们处理数字。两者结合起来,我们就可以编写更强大的程序了!

数值变量与操作

引言:生活中的数字

小明,你有没有想过,我们每天要和多少数字打交道?

  • 📅 早上7:30起床,7:50出门
  • 🚶 从家走到学校需要15分钟
  • 📝 语文考了85分,数学考了92分
  • 🛒 买一支铅笔5元,买一个本子12元
  • 🎮 游戏里你已经升到了15级

数字帮我们:

  • 📊 统计和比较:谁考得好?谁长得高?
  • 💰 计算和管理:买东西要付多少钱?零花钱够不够?
  • 📐 测量和描述:距离、时间、重量、温度
  • 🎯 解决问题:平均分是多少?还差多少分?

在这一章,我们将学习:

  • 什么是整数小数
  • 如何用Python做数学运算
  • 计算机是如何存储和计算数字的
  • 用编程解决数学应用题

👨‍🏫 给家长的辅导建议:

  • 这一章是数学和编程结合的重点章节
  • 建议让孩子先回忆数学课学过的内容:整数、小数、四则运算
  • 可以用生活中的场景引导:购物计算、时间计算、测量等
  • 本章融入了计算机如何存储数字的知识,这是理解计算机工作原理的基础
  • 如果孩子在某些数学概念上不太清楚,可以借此机会复习

数值的类型

在Python中,数字主要有两种类型:整数小数

1. 整数(int) - 完整的数字

整数就是没有小数部分的数字,可以是正数、负数或零。

# 正整数 - 计数、年龄、等级
age = 10              # 年龄
height_cm = 145       # 身高(厘米)
score = 95            # 分数
level = 8             # 游戏等级

# 负整数 - 欠款、温度零下、反向
temperature = -5      # 零下5度
balance = -100        # 欠款100元
elevator = -3         # 地下3层

# 零
zero = 0

💡 思考一下:

  • 为什么年龄用整数?你会说“我10岁半“吗?
  • 为什么温度可以是负数?温度计上负数在哪边?

👨‍🏫 给家长的知识补充:

  • int 是 integer(整数)的缩写
  • 整数用于计数场景(人数、物品数)和离散数据(年龄、等级)
  • 生活中大多数计数都是整数,你不能有“2.5个人“

🖥️ 计算机如何存储整数?

计算机用二进制(0和1)来存储数字。这里有一个简单的理解方式:

想象你有几个开关,每个开关只有“开“(1)和“关“(0)两种状态:

十进制 0  →  二进制 0000
十进制 1  →  二进制 0001
十进制 2  →  二进制 0010
十进制 3  →  二进制 0011
十进制 4  →  二进制 0100
十进制 5  →  二进制 0101
...
十进制 10 →  二进制 1010

👨‍🏫 给家长的讲解建议:

  • 小学阶段不需要深入理解二进制转换
  • 重点让孩子知道:计算机用0和1表示所有数字
  • 可以用“手指计数“类比:我们用10个手指计数(十进制),计算机用“开关“计数(二进制)
  • 4个开关可以表示0-15的数字(2⁴=16种可能)
  • 这就是为什么计算机存储容量是2、4、8、16、32、64等数字

2. 小数(float) - 精确的数字

小数也叫“浮点数“(float),是带小数点的数字。

# 测量数据 - 身高、体重、温度
height = 1.45         # 身高1.45米
weight = 38.5         # 体重38.5公斤
temperature = 36.5    # 体温36.5度

# 价格 - 金钱计算
price = 9.99          # 价格9.99元
discount = 0.85       # 8.5折扣

# 数学常数
pi = 3.14159          # 圆周率

# 科学计数法(表示很大或很小的数)
big_number = 3.14e10      # 3.14 × 10¹⁰ (314亿)
small_number = 1.23e-5    # 1.23 × 10⁻⁵ (0.0000123)

💡 思考一下:

  • 为什么身高用小数?(因为人在不断长高,可能是1.45米、1.46米…)
  • 为什么价格用小数?(因为金额需要精确,9.99元不等于10元)

👨‍🏫 给家长的知识补充:

  • float 是“浮点数“(floating point number)的缩写
  • “浮点“是因为小数点可以“浮动“到不同位置
  • 小数用于测量场景和连续数据
  • 重要:计算机中的小数可能有微小误差
    print(0.1 + 0.2)  # 输出: 0.30000000000000004
    
    这是因为计算机用二进制表示小数,有些十进制小数无法精确表示(类似1/3=0.333…) 暂时不需要深入理解原理,孩子只要知道“小数可能有微小误差“即可

🖥️ 小数在内存中的占用

  • 整数(int):通常占用4字节或8字节,取决于数字大小
  • 小数(float):通常占用8字节,可以表示很大范围的数字

可以简单理解为:

  • 整数盒子比较固定,装整数刚刚好
  • 小数盒子需要更复杂的结构,所以占用空间更大

判断数值类型

我们可以用 type() 函数查看一个变量的类型:

# 查看类型
i = 10
f = 3.14

print(type(i))  # 输出:<class 'int'>
print(type(f))  # 输出:<class 'float'>

# 判断类型
print(type(i) == int)    # 输出:True
print(type(f) == float)  # 输出:True

👨‍🏫 给家长的讲解建议:

  • type() 函数可以帮助检查变量的类型
  • 如果孩子觉得 type(i) == int 太复杂,可以暂时不学
  • 重点让孩子知道:整数是 int,小数是 float

数学练习:应用题入门

在深入学习运算之前,让我们先看看如何用编程解决数学应用题。

练习1:年龄问题

问题:小明今年10岁,爸爸今年38岁。5年后,爸爸比小明大多少岁?

思路:

  • 5年后小明的年龄 = 10 + 5
  • 5年后爸爸的年龄 = 38 + 5
  • 年龄差 = 爸爸的年龄 - 小明的年龄
# 现在的年龄
xiaoming = 10
dad = 38

# 5年后的年龄
years = 5
xiaoming_future = xiaoming + years
dad_future = dad + years

# 计算年龄差
diff = dad_future - xiaoming_future

print(f"现在:小明{xiaoming}岁,爸爸{dad}岁")
print(f"5年后:小明{xiaoming_future}岁,爸爸{dad_future}岁")
print(f"年龄差:{diff}岁")

运行结果:

现在:小明10岁,爸爸38岁
5年后:小明15岁,爸爸43岁
年龄差:28岁

💡 思考一下:

  • 为什么要先算各自的年龄,再算差值?
  • 能不能直接用 38 - 10?为什么?

👨‍🏫 给家长的教学提示:

  • 这是典型的“年龄问题“
  • 关键点:年龄差永远不变
  • 可以让孩子先口算,再用编程验证
  • 扩展练习:两人年龄和是多少?多少年前爸爸年龄是小明的3倍?

练习2:价格问题

问题:一支铅笔5元,一个本子12元。买3支铅笔和2个本子,一共多少钱?

思路:

  • 铅笔总价 = 单价 × 数量
  • 本子总价 = 单价 × 数量
  • 总金额 = 铅笔总价 + 本子总价
# 单价
pencil_price = 5
notebook_price = 12

# 数量
pencil_count = 3
notebook_count = 2

# 计算
pencil_total = pencil_price * pencil_count
notebook_total = notebook_price * notebook_count
total = pencil_total + notebook_total

print(f"铅笔:{pencil_price}元 × {pencil_count} = {pencil_total}元")
print(f"本子:{notebook_price}元 × {notebook_count} = {notebook_total}元")
print(f"合计:{total}元")

运行结果:

铅笔:5元 × 3 = 15元
本子:12元 × 2 = 24元
合计:39元

💡 思考一下:

  • 如果有50元,够不够买?还剩多少?
  • 如果所有商品打8折,要付多少钱?

👨‍🏫 给家长的教学提示:

  • 这是“总价 = 单价 × 数量“的基础应用
  • 可以扩展到购物场景:带100元买东西,够不够?
  • 逐步引导孩子理解:先算各项,再求和

练习3:速度问题

问题:小明从家走到学校,距离是1200米,他每分钟走80米。需要多少分钟?

思路:

  • 时间 = 距离 ÷ 速度
# 已知条件
distance = 1200  # 距离(米)
speed = 80       # 速度(米/分钟)

# 计算时间
time = distance / speed

print(f"距离:{distance}米")
print(f"速度:{speed}米/分钟")
print(f"需要时间:{time}分钟")

运行结果:

距离:1200米
速度:80米/分钟
需要时间:15.0分钟

💡 思考一下:

  • 为什么结果是15.0而不是15?(因为我们用了 / 除法)
  • 如果要15分钟,应该用什么运算?(用 // 整除)

👨‍🏫 给家长的教学提示:

  • 这是“路程、速度、时间“的三个基本关系:
    • 路程 = 速度 × 时间
    • 时间 = 路程 ÷ 速度
    • 速度 = 路程 ÷ 时间
  • 可以结合物理课或数学课的知识讲解
  • 注意单位的统一(米、千米、分钟、小时)

数值的基本运算

Python支持所有的基本数学运算。

算术运算符

a = 10
b = 3

# 加法 +
print(a + b)   # 输出:13

# 减法 -
print(a - b)   # 输出:7

# 乘法 *
print(a * b)   # 输出:30

# 除法 /(结果总是小数)
print(a / b)   # 输出:3.3333333333333335

# 整除 //(只保留整数部分)
print(a // b)  # 输出:3

# 求余数 %(模运算)
print(a % b)   # 输出:1(因为 10 = 3×3 + 1)

# 幂运算 **(乘方)
print(a ** b)  # 输出:1000(10的3次方)

💡 实物类比:

  • 整除 //:你有10颗糖,平均分给3个人,每人得3颗(整除)
  • 求余 %:你有10颗糖,分给3个人,每人3颗,还剩1颗(余数)
  • 幂运算 **:正方形的面积是边长的平方(5² = 5 × 5 = 25)

👨‍🏫 给家长的教学建议:

  • / 是普通除法,结果总是小数(float)
  • // 是整除,结果是整数(int),相当于数学中的“商“
  • % 是求余数,可以理解为“除不尽剩下的部分“
  • ** 是幂运算,2 ** 3 就是 2³ = 8
  • 重点:用分糖果、分蛋糕等实物类比,帮助孩子理解整除和余数

运算的优先级

和数学一样,Python的运算也有优先级:

# 先乘除,后加减
result = 2 + 3 * 4
print(result)  # 输出:14(不是20)

# 使用括号改变优先级
result = (2 + 3) * 4
print(result)  # 输出:20

# 幂运算优先级最高
result = 2 ** 3 * 4
print(result)  # 输出:32(先算2³=8,再算8×4)

# 同级运算从左到右
result = 10 - 3 - 2
print(result)  # 输出:5(先算10-3=7,再算7-2=5)

优先级顺序(从高到低):

  1. 括号 ()
  2. 幂运算 **
  3. 乘除 * / // %
  4. 加减 + -

👨‍🏫 给家长的教学建议:

  • 建议孩子多使用括号,即使不是必须的,也可以让代码更清晰
  • 如果不确定优先级,就用括号明确表示
  • 可以和数学课学的运算优先级对比,帮助理解

🖥️ 计算机的计算能力

你可能好奇,计算机计算有多快?让我们来测试一下!

测试1:计算速度对比

import time

# 测试1:简单的加法
start = time.time()
result = 0
for i in range(10000000):  # 一千万次
    result = result + 1
end = time.time()

print(f"计算机执行一千万次加法用时:{end - start:.4f}秒")
print(f"也就是每秒可以执行约:{10000000 / (end - start):.0f}次加法")

运行结果(根据计算机不同会有差异):

计算机执行一千万次加法用时:0.5123秒
也就是每秒可以执行约:19519837次加法

💡 也就是说:

  • 普通计算机每秒可以执行几千万次简单运算
  • 如果是人来做,每秒做1次加法,一千万次需要约4个月!
  • 这就是为什么计算机擅长做重复性工作

测试2:大数计算

# 计算很大的数字
big_number = 12345678901234567890 ** 100
print(f"计算大数的100次方...")
print(f"结果有{len(str(big_number))}位数字!")

👨‍🏫 给家长的知识补充:

  • CPU(中央处理器)是计算机的“大脑“,负责计算
  • CPU的主频表示每秒可以执行的周期数
    • 1 GHz = 每秒10亿次周期
    • 3 GHz = 每秒30亿次周期
  • 但不是说CPU每秒只能做30亿次运算
    • 现代CPU有多个“核心“(像有多个大脑)
    • 每个核心可以同时处理多个任务
    • 所以实际运算能力远超主频数字

实践1:基础计算器

让我们用学到的知识做一个多功能计算器:

# 基础计算器
print("=" * 40)
print("  🧮 小小计算器 🧮  ")
print("=" * 40)
print()

# 获取输入
num1 = input("请输入第一个数字:")
num2 = input("请输入第二个数字:")

# 转换成数值
num1 = float(num1)
num2 = float(num2)

# 计算
print()
print("📊 计算结果:")
print("-" * 35)
print(f"{num1} + {num2} = {num1 + num2}")
print(f"{num1} - {num2} = {num1 - num2}")
print(f"{num1} × {num2} = {num1 * num2}")
print(f"{num1} ÷ {num2} = {num1 / num2:.4f}")
print(f"{num1} 整除 {num2} = {num1 // num2}")
print(f"{num1} 余 {num2} = {num1 % num2}")
print(f"{num1} 的 {num2} 次方 = {num1 ** num2:.4f}")
print("-" * 35)

运行示例:

========================================
  🧮 小小计算器 🧮
========================================

请输入第一个数字:10
请输入第二个数字:3

📊 计算结果:
-----------------------------------
10.0 + 3.0 = 13.0
10.0 - 3.0 = 7.0
10.0 × 3.0 = 30.0
10.0 ÷ 3.0 = 3.3333
10.0 整除 3.0 = 3.0
10.0 余 3.0 = 1.0
10.0 的 3.0 次方 = 1000.0
-----------------------------------

👨‍🏫 给家长的教学建议:

  • 这里我们用 float() 而不是 int(),因为用户可能输入小数
  • f-string 让输出格式更清晰
  • 可以让孩子尝试输入不同的数字,观察结果
  • 可以讨论:为什么除法结果有很多小数?(因为 10/3 = 3.333… 无限循环)

数学函数

Python内置了一些数学函数,可以帮我们完成复杂的计算。

1. 绝对值、最大值、最小值

# 绝对值 - 数轴上的距离
print(abs(-5))    # 输出:5
print(abs(3.14))  # 输出:3.14
print(abs(0))     # 输出:0

# 最大值
print(max(1, 5, 3))      # 输出:5
print(max(10, 20, 30))   # 输出:30
print(max(-5, -2, -10))  # 输出:-2

# 最小值
print(min(1, 5, 3))      # 输出:1
print(min(10, 20, 30))   # 输出:10
print(min(-5, -2, -10))  # 输出:-10

# 也可以用于列表(后面会学)
numbers = [3, 1, 4, 1, 5]
print(max(numbers))  # 输出:5
print(min(numbers))  # 输出:1

💡 实物类比:

  • 绝对值:小明向东走了5米,小红向西走了5米,他们距离起点都是5米
  • 最大值:考试成绩,最高分是多少?
  • 最小值:气温记录,最低气温是多少?

👨‍🏫 给家长的教学建议:

  • abs 是 absolute value(绝对值)的缩写
  • 绝对值可以用“距离原点的距离“来解释:-5 到 0 的距离是 5
  • maxmin 可以接受多个数字或一个列表
  • 可以结合实际场景:班级最高身高、最低气温、最高分等

2. 四舍五入

# 四舍五入
print(round(3.14))      # 输出:3(默认保留整数)
print(round(3.5))       # 输出:4(.5会进位)
print(round(3.14159, 2))  # 输出:3.14(保留2位小数)
print(round(3.14159, 3))  # 输出:3.142(保留3位小数)
print(round(3.14159, 4))  # 输出:3.1416(保留4位小数)

# 负数也可以
print(round(-3.5))      # 输出:-4

# 金钱计算常用
price = 9.99
quantity = 3
total = price * quantity
print(f"总价:{total}元")           # 输出:总价:29.970000000000002元
print(f"总价:{round(total, 2)}元") # 输出:总价:29.97元

👨‍🏫 给家长的知识补充:

  • round() 的第二个参数是保留的小数位数
  • “四舍五入“在Python中其实是“银行家舍入”(.5会舍入到最近的偶数)
    • 例如:2.5 → 2,3.5 → 4(而不是2.5→3,3.5→4)
  • 这个细节孩子不需要深究,知道是四舍五入即可
  • 在金钱计算时,建议用 round(total, 2) 保留两位小数

3. 幂运算

# 幂运算(也可以用 **)
print(pow(2, 3))  # 输出:8(2的3次方)
print(pow(5, 2))  # 输出:25(5的2次方)
print(pow(10, 6)) # 输出:1000000(10的6次方)

# 平方根
print(4 ** 0.5)   # 输出:2.0(4的平方根)
print(9 ** 0.5)   # 输出:3.0(9的平方根)
print(16 ** 0.5)  # 输出:4.0(16的平方根)

💡 实际应用:

  • 平方:正方形的面积 = 边长²
  • 立方:正方体的体积 = 边长³
  • 平方根:如果一个正方形面积是16,边长是多少?(√16 = 4)

实践2:成绩统计器

让我们用学到的函数做一个成绩统计器:

# 成绩统计器
print("=" * 40)
print("  📊 成绩统计器 📊  ")
print("=" * 40)
print()

# 获取成绩
chinese = input("请输入语文成绩:")
math_score = input("请输入数学成绩:")
english = input("请输入英语成绩:")
science = input("请输入科学成绩:")

# 转换成数值
chinese = float(chinese)
math_score = float(math_score)
english = float(english)
science = float(science)

# 计算
total = chinese + math_score + english + science
average = total / 4
highest = max(chinese, math_score, english, science)
lowest = min(chinese, math_score, english, science)

# 输出
print()
print("📈 统计结果:")
print("-" * 35)
print(f"语文:{chinese:.1f}分")
print(f"数学:{math_score:.1f}分")
print(f"英语:{english:.1f}分")
print(f"科学:{science:.1f}分")
print("-" * 35)
print(f"总分:{total:.1f}分")
print(f"平均分:{average:.2f}分")
print(f"最高分:{highest:.1f}分")
print(f"最低分:{lowest:.1f}分")
print(f"总分差:{highest - lowest:.1f}分")
print("-" * 35)

运行示例:

========================================
  📊 成绩统计器 📊
========================================

请输入语文成绩:85
请输入数学成绩:92
请输入英语成绩:88
请输入科学成绩:90

📈 统计结果:
-----------------------------------
语文:85.0分
数学:92.0分
英语:88.0分
科学:90.0分
-----------------------------------
总分:355.0分
平均分:88.75分
最高分:92.0分
最低分:85.0分
总分差:7.0分
-----------------------------------

👨‍🏫 给家长的教学建议:

  • 这个练习综合运用了:输入输出、类型转换、四则运算、函数
  • 可以讨论:如果平均分低于60分,应该提示警告?(为第7章条件语句做铺垫)
  • 可以扩展:添加更多科目,计算排名等

练习1:温度转换器

任务:编写一个温度转换程序

  1. 询问用户摄氏温度
  2. 转换成华氏温度(公式:F = C × 1.8 + 32)
  3. 输出结果,保留一位小数
📝 点击查看提示

提示:

  • float() 获取小数温度
  • 使用公式计算
  • 用 f-string 格式化输出,如 {fahrenheit:.1f} 可以保留一位小数
📝 点击查看参考答案
# 温度转换器
print("=" * 40)
print("  🌡️ 温度转换器 🌡️  ")
print("=" * 40)
print()

# 获取摄氏温度
celsius = input("请输入摄氏温度:")
celsius = float(celsius)

# 转换成华氏温度
fahrenheit = celsius * 1.8 + 32

# 输出结果
print()
print(f"📍 {celsius}°C = {fahrenheit:.1f}°F")

运行示例:

========================================
  🌡️ 温度转换器 🌡️
========================================

请输入摄氏温度:25

📍 25.0°C = 77.0°F

👨‍🏫 给家长的教学建议:

  • .1f 表示保留1位小数,.2f 表示保留2位小数
  • 可以和孩子讨论:为什么用小数而不是整数?(因为温度可能是小数)
  • 可以扩展:添加华氏转摄氏的功能(C = (F - 32) / 1.8)
  • 这是“一次函数“的实际应用:y = ax + b

💡 数学知识:

  • 摄氏温度(°C)和华氏温度(°F)的换算关系
  • 这是一个线性关系,可以用一次函数表示
  • 0°C = 32°F(水的冰点)
  • 100°C = 212°F(水的沸点)

练习2:购物折扣计算器

任务:编写一个购物清单程序

  1. 询问3件商品的价格
  2. 计算总价
  3. 如果总价超过100元,打9折
  4. 输出原价、折扣、最终价格
📝 点击查看提示

提示:

  • float() 获取价格
  • if 语句判断是否打折(第7章会详细学)
  • 9折就是原价 × 0.9,或者减去10%
📝 点击查看参考答案
# 购物清单程序
print("=" * 40)
print("  🛒 购物清单 🛒  ")
print("=" * 40)
print()

# 获取价格
price1 = input("请输入第1件商品价格:")
price2 = input("请输入第2件商品价格:")
price3 = input("请输入第3件商品价格:")

# 转换成数值
price1 = float(price1)
price2 = float(price2)
price3 = float(price3)

# 计算总价
total = price1 + price2 + price3

# 判断折扣
discount = 0
final_price = total

if total > 100:
    discount = total * 0.1  # 9折就是减去10%
    final_price = total - discount
    print("🎉 恭喜!消费满100元,享受9折优惠!")

# 输出
print()
print("💰 账单:")
print("-" * 35)
print(f"商品1:¥{price1:.2f}")
print(f"商品2:¥{price2:.2f}")
print(f"商品3:¥{price3:.2f}")
print("-" * 35)
print(f"原价:¥{total:.2f}")
print(f"折扣:-¥{discount:.2f}")
print(f"最终:¥{final_price:.2f}")
print("-" * 35)

运行示例:

========================================
  🛒 购物清单 🛒
========================================

请输入第1件商品价格:35
请输入第2件商品价格:45
请输入第3件商品价格:30
🎉 恭喜!消费满100元,享受9折优惠!

💰 账单:
-----------------------------------
商品1:¥35.00
商品2:¥45.00
商品3:¥30.00
-----------------------------------
原价:¥110.00
折扣:-¥11.00
最终:¥99.00
-----------------------------------

👨‍🏫 给家长的教学建议:

  • 这个练习用到了条件语句(第7章内容)
  • 如果孩子还没学,可以先简化:不打折,只计算总价
  • 可以扩展:
    • 添加更多商品
    • 多级折扣(满100打9折,满200打8折)
    • 会员折扣
  • 这是“分段函数“的实际应用

💡 数学知识:

  • 折扣计算:原价 × 折扣率 = 折后价
  • 9折 = 90% = 0.9
  • 折扣金额 = 原价 × (1 - 折扣率)
  • 这是“百分比“的实际应用

数值的格式化输出

在输出数值时,我们可以控制它的显示格式。

1. 控制小数位数

pi = 3.1415926

# 保留2位小数
print(f"π ≈ {pi:.2f}")  # 输出:π ≈ 3.14

# 保留4位小数
print(f"π ≈ {pi:.4f}")  # 输出:π ≈ 3.1416

# 不保留小数
print(f"π ≈ {pi:.0f}")  # 输出:π ≈ 3

# 自动保留(不指定)
print(f"π = {pi}")  # 输出:π = 3.1415926

2. 对齐和宽度

number = 42

# 右对齐,宽度10
print(f"[{number:>10}]")  # 输出:[        42]

# 左对齐,宽度10
print(f"[{number:<10}]")  # 输出:[42        ]

# 居中,宽度10
print(f"[{number:^10}]")  # 输出:[    42    ]

# 小数也可以对齐
pi = 3.14
print(f"[{pi:^10.2f}]")  # 输出:[   3.14   ]

3. 添加千位分隔符

big_number = 1234567890

# 添加千位分隔符
print(f"{big_number:,}")  # 输出:1,234,567,890

# 小数也可以
price = 1234.56
print(f"¥{price:,.2f}")  # 输出:¥1,234.56

# 人口统计
population = 1400000000
print(f"中国人口约:{population:,}人")
# 输出:中国人口约约:1,400,000,000人

👨‍🏫 给家长的知识补充:

  • 格式化字符串的语法是 f"{变量:格式}"
  • :.2f 表示保留2位小数
  • :>10 表示右对齐,宽度10
  • :, 表示添加千位分隔符
  • 这些格式可以组合使用,如 :>10,.2f
  • 暂时不需要孩子记住所有格式,需要时再查阅

实践3:几何计算器

让我们做一个几何图形计算器,复习数学中的几何知识!

梯形计算器

问题:梯形的上底是5cm,下底是8cm,高是3cm,求面积和周长?

# 梯形计算器
print("=" * 45)
print("  📐 梯形计算器 📐  ")
print("=" * 45)
print()
print("梯形有四条边:上底、下底、两条腰")
print()

# 获取输入
top = input("请输入上底的长度(cm):")
bottom = input("请输入下底的长度(cm):")
left = input("请输入左侧腰的长度(cm):")
right = input("请输入右侧腰的长度(cm):")
height = input("请输入梯形的高度(cm):")

# 转换成数值
top = float(top)
bottom = float(bottom)
left = float(left)
right = float(right)
height = float(height)

# 计算
# 梯形面积公式:面积 = (上底 + 下底) × 高 ÷ 2
area = (top + bottom) * height / 2

# 梯形周长公式:周长 = 上底 + 下底 + 左腰 + 右腰
perimeter = top + bottom + left + right

# 输出
print()
print("📊 计算结果:")
print("-" * 40)
print(f"上底:{top:.2f} cm")
print(f"下底:{bottom:.2f} cm")
print(f"左腰:{left:.2f} cm")
print(f"右腰:{right:.2f} cm")
print(f"高度:{height:.2f} cm")
print("-" * 40)
print(f"面积:{area:.2f} cm²")
print(f"周长:{perimeter:.2f} cm")
print("-" * 40)

运行示例:

=============================================
  📐 梯形计算器 📐
=============================================

梯形有四条边:上底、下底、两条腰

请输入上底的长度(cm):5
请输入下底的长度(cm):8
请输入左侧腰的长度(cm):4
请输入右侧腰的长度(cm):4
请输入梯形的高度(cm):3

📊 计算结果:
----------------------------------------
上底:5.00 cm
下底:8.00 cm
左腰:4.00 cm
右腰:4.00 cm
高度:3.00 cm
----------------------------------------
面积:19.50 cm²
周长:21.00 cm
----------------------------------------

💡 数学知识:

  • 梯形面积公式:S = (a + b) × h ÷ 2
    • a = 上底,b = 下底,h = 高
  • 梯形周长公式:C = a + b + c + d
    • a = 上底,b = 下底,c = 左腰,d = 右腰

👨‍🏫 给家长的教学建议:

  • 可以用梯形图片或实物帮助孩子理解
  • 可以扩展:检查输入是否合理(如上底、下底、高度不能为负数)
  • 可以讨论:如果两条腰相等,是什么梯形?(等腰梯形)
  • 可以联系实际:梯形的河堤、梯形的田地等

圆形计算器

问题:一个圆的半径是5cm,求面积和周长?

# 圆形计算器
print("=" * 40)
print("  ⭕ 圆形计算器 ⭕  ")
print("=" * 40)
print()

# 获取半径
radius = input("请输入圆的半径(cm):")
radius = float(radius)

# 圆周率
pi = 3.141592653589793

# 计算
# 圆面积公式:面积 = π × 半径²
area = pi * radius ** 2

# 圆周长公式:周长 = 2 × π × 半径
circumference = 2 * pi * radius

# 输出
print()
print("📊 计算结果:")
print("-" * 35)
print(f"半径:{radius:.2f} cm")
print(f"圆周率π:{pi:.10f}")
print("-" * 35)
print(f"面积:{area:.2f} cm²")
print(f"周长:{circumference:.2f} cm")
print("-" * 35)

运行示例:

========================================
  ⭕ 圆形计算器 ⭕
========================================

请输入圆的半径(cm):5

📊 计算结果:
-----------------------------------
半径:5.00 cm
圆周率π:3.1415926536
-----------------------------------
面积:78.54 cm²
周长:31.42 cm
-----------------------------------

💡 数学知识:

  • 圆面积公式:S = πr²
    • π = 圆周率 ≈ 3.14
    • r = 半径
  • 圆周长公式:C = 2πr
    • 或者 C = πd(d = 直径)

👨‍🏫 给家长的教学建议:

  • π是一个无理数,无限不循环小数
  • 实际计算中通常取 π ≈ 3.14 或 π ≈ 3.1416
  • Python中的 math.pi 提供了更精确的π值(第13章会学)

常见错误和调试

在编程中,错误是学习的好机会!让我们看看常见的错误。

错误1:忘记类型转换

# ❌ 错误示例
a = input("请输入第一个数字:")
b = input("请输入第二个数字:")
print(a + b)  # 错误!这是字符串拼接,不是加法
# 如果输入10和3,会输出"103",而不是13

# ✅ 正确做法
a = input("请输入第一个数字:")
b = input("请输入第二个数字:")
a = float(a)  # 转换成数值
b = float(b)
print(a + b)  # 正确!这是数值加法

👨‍🏫 给家长的讲解建议:

  • 这是最常见的错误!
  • input() 总是返回字符串,即使输入的是数字
  • 必须用 int()float() 转换成数值才能计算
  • 让孩子亲自运行错误代码,理解为什么出错

错误2:整数除法vs小数除法

# 问题:想要得到整数的商
a = 10
b = 3

# ❌ 用普通除法
print(a / b)  # 输出:3.3333333333333335

# ✅ 用整除
print(a // b)  # 输出:3

# ✅ 或者用int()转换
print(int(a / b))  # 输出:3

👨‍🏫 给家长的讲解建议:

  • / 总是得到小数
  • 如果想要整数结果,用 //int()
  • 根据实际需求选择合适的方法

错误3:小数精度问题

# 小数可能有微小误差
print(0.1 + 0.2)
# 输出:0.30000000000000004
# 而不是我们期待的0.3

# ✅ 解决方法1:四舍五入
result = 0.1 + 0.2
print(round(result, 2))  # 输出:0.3

# ✅ 解决方法2:用整数计算(分转元)
fen_1 = 10  # 0.1元 = 10分
fen_2 = 20  # 0.2元 = 20分
total_fen = fen_1 + fen_2
total_yuan = total_fen / 100
print(total_yuan)  # 输出:0.3

👨‍🏫 给家长的讲解建议:

  • 这是计算机表示小数的方式造成的
  • 在金钱计算时,建议用“分“做单位,避免小数误差
  • 或者用 round() 四舍五入
  • 如果孩子觉得困惑,可以说“计算机有时候会有一点小误差“

章节小结

恭喜你完成了这一章!让我们回顾一下学到的内容。

📚 核心概念

  1. 整数(int):没有小数部分的数字,用于计数
  2. 小数(float):带小数点的数字,用于测量
  3. 算术运算:加+、减-、乘*、除/、整除//、求余%、幂**
  4. 运算优先级:括号 > 幂运算 > 乘除 > 加减

🛠️ 常用函数

  • abs(x):绝对值
  • max(a, b, c):最大值
  • min(a, b, c):最小值
  • round(x, n):四舍五入,保留n位小数
  • pow(x, y):x的y次方

🖥️ 计算机知识

  • 计算机用二进制(0和1)存储数字
  • CPU是计算机的“大脑“,负责计算
  • 现代CPU每秒可以执行数十亿次运算
  • 整数和小数在计算机中的存储方式不同

💡 数学知识应用

  • 年龄问题:年龄差永远不变
  • 价格问题:总价 = 单价 × 数量
  • 速度问题:时间 = 距离 ÷ 速度
  • 几何公式:
    • 梯形面积:S = (上底 + 下底) × 高 ÷ 2
    • 圆面积:S = πr²
    • 圆周长:C = 2πr

⚠️ 重要提示

  • / 是普通除法,// 是整除
  • % 是求余数
  • input() 得到的是字符串,需要转换成数值才能计算
  • 小数可能有微小精度误差,可以用 round() 解决

🎯 挑战练习

选择1-2个你感兴趣的题目完成!

1. 复利计算器 💰

输入本金、年利率、年数,计算复利。

  • 公式:最终金额 = 本金 × (1 + 利率)^年数
  • 例如:1000元,年利率5%,存3年,最终是多少?
💡 提示
  • 利率5%要写成0.05
  • ** 计算幂
📝 参考答案
# 复利计算器
print("=" * 40)
print("  💰 复利计算器 💰  ")
print("=" * 40)
print()

# 获取输入
principal = input("请输入本金(元):")
rate = input("请输入年利率(如5%输入5):")
years = input("请输入存款年数:")

# 转换
principal = float(principal)
rate = float(rate) / 100  # 5% → 0.05
years = int(years)

# 计算复利
amount = principal * (1 + rate) ** years
interest = amount - principal

# 输出
print()
print("📊 计算结果:")
print("-" * 35)
print(f"本金:¥{principal:.2f}")
print(f"年利率:{rate*100:.1f}%")
print(f"存款年数:{years}年")
print("-" * 35)
print(f"利息:¥{interest:.2f}")
print(f"本息合计:¥{amount:.2f}")
print("-" * 35)

💡 数学知识:

  • 复利公式:F = P(1 + r)^n
    • F = 未来金额(Future Value)
    • P = 本金(Principal)
    • r = 利率(Rate)
    • n = 期数(Number of periods)
  • 复利是“利滚利“,利息也会产生利息

2. BMI计算器 ⚖️

输入身高和体重,计算BMI,并判断是否正常。

  • 公式:BMI = 体重 ÷ 身高²
  • 判断标准:
    • BMI < 18.5:偏瘦
    • 18.5 ≤ BMI < 24:正常
    • BMI ≥ 24:偏胖
💡 提示
  • 身高单位是米,不是厘米
  • 用条件语句判断(第7章会详细学)
📝 参考答案
# BMI计算器
print("=" * 40)
print("  ⚖️ BMI计算器 ⚖️  ")
print("=" * 40)
print()

# 获取输入
height = input("请输入身高(米):")
weight = input("请输入体重(公斤):")

# 转换
height = float(height)
weight = float(weight)

# 计算BMI
bmi = weight / (height ** 2)

# 判断
if bmi < 18.5:
    status = "偏瘦"
elif bmi < 24:
    status = "正常"
else:
    status = "偏胖"

# 输出
print()
print("📊 计算结果:")
print("-" * 35)
print(f"身高:{height:.2f} 米")
print(f"体重:{weight:.1f} 公斤")
print("-" * 35)
print(f"BMI指数:{bmi:.1f}")
print(f"健康状态:{status}")
print("-" * 35)

💡 数学知识:

  • BMI = Body Mass Index(身体质量指数)
  • 是衡量一个人胖瘦的标准
  • 公式:BMI = 体重 ÷ 身高²
  • 注意单位:体重用kg,身高用m

3. 找零计算器 💵

输入商品价格和支付金额,计算应该找多少零钱。

  • 尽量用大面额纸币(100元、50元、20元、10元、5元、1元)
  • 例如:商品123元,支付200元,找77元(50+20+5+1+1)
💡 提示
  • 用整除和求余计算各种面额的数量
  • 77 ÷ 50 = 1余27 → 1张50元
  • 27 ÷ 20 = 1余7 → 1张20元
📝 参考答案
# 找零计算器
print("=" * 40)
print("  💵 找零计算器 💵  ")
print("=" * 40)
print()

# 获取输入
price = input("请输入商品价格(元):")
paid = input("请输入支付金额(元):")

# 转换
price = float(price)
paid = float(paid)

# 计算找零
change = paid - price

# 计算各种面额
change_100 = int(change // 100)
change = change % 100

change_50 = int(change // 50)
change = change % 50

change_20 = int(change // 20)
change = change % 20

change_10 = int(change // 10)
change = change % 10

change_5 = int(change // 5)
change = change % 5

change_1 = int(change // 1)
change = change % 1

# 输出
print()
print("📊 找零方案:")
print("-" * 35)
print(f"商品价格:¥{price:.2f}")
print(f"支付金额:¥{paid:.2f}")
print(f"找零总额:¥{paid - price:.2f}")
print("-" * 35)
if change_100 > 0:
    print(f"100元纸币:{change_100}张")
if change_50 > 0:
    print(f"50元纸币:{change_50}张")
if change_20 > 0:
    print(f"20元纸币:{change_20}张")
if change_10 > 0:
    print(f"10元纸币:{change_10}张")
if change_5 > 0:
    print(f"5元纸币:{change_5}张")
if change_1 > 0:
    print(f"1元硬币:{change_1}枚")
print("-" * 35)

💡 数学知识:

  • 这是“贪心算法“的简单应用
  • 每次尽量用最大面额
  • 用整除得到数量,用求余得到剩余金额

🎉 给家长的教学建议

教学重点

  1. 整数和小数的区别:什么时候用整数,什么时候用小数
  2. 类型转换:这是最容易出错的地方,要强调
  3. 整除和求余:对孩子来说比较抽象,需要用实物类比
  4. 运算优先级:可以用数学课的知识对比
  5. 数学融入:通过编程复习数学知识,一举两得

常见问题及解答

Q1: 什么时候用整数,什么时候用小数?

  • A: 计数、年龄、排名用整数;测量、价格、精确计算用小数。

Q2: 为什么 10 / 3 结果不是整数?

  • A: 因为 / 是普通除法,结果总是小数。如果想要整数,用 // 整除。

Q3: 求余数有什么用?

  • A: 可以用来判断能不能整除、判断奇数偶数、分东西等。

Q4: 为什么小数计算会有误差?

  • A: 这是计算机表示小数的方式造成的。如果需要精确计算,可以用 decimal 模块(进阶内容)。

Q5: 如何判断孩子是否掌握了?

  • A: 看孩子能否独立完成:
    • 基础计算器
    • 至少一个应用题练习
    • 理解为什么要类型转换

练习建议

  • 基础练习:让孩子用数值计算制作各种“计算器“

    • 温度转换
    • 货币兑换
    • 成绩统计
  • 生活应用:

    • 购物:计算总价、折扣
    • 烹饪:食谱换算(2人份→4人份)
    • 运动:步数、距离、速度
  • 数学复习:

    • 用编程验证数学题的答案
    • 把数学应用题写成程序
    • 用程序生成数学练习题

下一步学习

完成这一章后,孩子应该:

  • ✅ 理解整数和小数的区别
  • ✅ 掌握基本的算术运算
  • ✅ 能解决简单的数学应用题
  • ✅ 了解计算机如何存储和计算数字

下一章,我们将学习布尔变量(Bool),这是编程中做判断的基础!

  • 什么是“真“和“假“
  • 如何比较大小
  • 如何进行逻辑判断
  • 为学习条件语句做准备

🔗 下一章:Bool变量与条件判断

在下一章中,我们将学习:

  • 什么是布尔值(True和False)
  • 比较运算符(>、<、==、!=)
  • 逻辑运算符(与、或、非)
  • 布尔值在判断中的应用

数值帮我们处理计算,布尔值帮我们做判断。两者结合起来,我们就可以编写更智能的程序了!

Bool变量与操作

引言

在前面的章节中,我们学习了两种数据类型:

  • 字符串:处理文字信息
  • 数值:处理数字和计算

今天我们要学习第三种重要的数据类型:布尔值(bool)。

布尔值就像是计算机的“答案“:要么是“对“(True),要么是“错“(False)。布尔值只有这两个值,用来表示“是“或“否“、“真“或“假”。

在生活中,布尔值无处不在:

  • 今天下雨了吗?(是/否)
  • 你的年龄超过10岁了吗?(是/否)
  • 这个答案正确吗?(对/错)
  • 游戏通关了吗?(成功/失败)

这一章,我们要深入了解:

  • 什么是布尔值(True和False)
  • 如何使用比较运算符得到布尔值
  • 如何使用逻辑运算符组合布尔值
  • 布尔值在实际编程中的应用

布尔值的基本概念

True 和 False

在Python中,布尔值只有两个:

  • True:表示“真“、“对”、“是”
  • False:表示“假“、“错”、“否”
# 直接使用布尔值
print(True)   # 输出:True
print(False)  # 输出:False

# 布尔变量
is_raining = True
is_sunny = False

print(is_raining)  # 输出:True
print(is_sunny)    # 输出:False

注意

  • TrueFalse 的首字母必须大写!
  • 它们不是字符串,不需要引号
  • 它们不是数字,不能用于计算

👨‍🏫 给家长的知识补充:

  • bool 是“布尔值“(boolean)的缩写,来自数学家乔治·布尔的名字
  • 布尔值可以用“开关“来类比:要么开(True),要么关(False)
  • 强调首字母大写:True 不是 true,False 不是 false
  • 暂时不需要深入讲解布尔代数,孩子只要知道 True/False 即可

🖥️ 布尔值在计算机中的表示

在计算机内部,布尔值是用数字来表示的:

  • True 通常表示为 1
  • False 通常表示为 0
# 布尔值可以转换成数字
print(int(True))   # 输出:1
print(int(False))  # 输出:0

# 数字也可以转换成布尔值
print(bool(1))     # 输出:True
print(bool(0))     # 输出:False

💡 也就是说:

  • 计算机本质上只认识 0 和 1
  • 所有的“真“和“假“,在计算机里都是用 0 和 1 存储的
  • 这就是为什么计算机用二进制(0和1)来工作

👨‍🏫 给家长的讲解建议:

  • 小学阶段不需要深入理解二进制和布尔代数
  • 重点让孩子知道:计算机用数字(0和1)来表示真假
  • 可以用“电灯开关“类比:开(1/True),关(0/False)
  • 这为后续理解计算机工作原理打下基础

布尔值的类型

# 查看布尔值的类型
print(type(True))    # 输出:<class 'bool'>
print(type(False))   # 输出:<class 'bool'>

# 判断是否为布尔类型
print(type(True) == bool)   # 输出:True
print(type(1) == bool)      # 输出:False

比较运算符

比较运算符用来比较两个值,结果是一个布尔值(True或False)。

1. 相等和不等

# 相等(==)
print(5 == 5)    # 输出:True
print(5 == 3)    # 输出:False
print("hello" == "hello")  # 输出:True
print("Hello" == "hello")   # 输出:False(大小写不同)

# 不等(!=)
print(5 != 3)    # 输出:True
print(5 != 5)    # 输出:False
print("cat" != "dog")  # 输出:True

重要提示= 是赋值,== 是比较!

# 错误示例
a = 5
if a = 5:  # 错误!应该用 ==
    print("yes")

# 正确示例
a = 5
if a == 5:  # 正确
    print("yes")

给家长的小贴士

  • == 是比较运算,问“相等吗?“
  • != 是“不等于“,来自键盘上的 !(有些键盘 !1 在同一个键)
  • 这是初学者最容易犯的错误:用 = 代替 ==
  • 可以用类比:= 是“把东西放进盒子“,== 是“比较两个东西“

2. 大小比较

# 大于(>)
print(5 > 3)    # 输出:True
print(5 > 10)   # 输出:False

# 小于(<)
print(5 < 10)   # 输出:True
print(5 < 3)    # 输出:False

# 大于等于(>=)
print(5 >= 5)   # 输出:True
print(5 >= 10)  # 输出:False

# 小于等于(<=)
print(5 <= 5)   # 输出:True
print(5 <= 3)   # 输出:False

给家长的小贴士

  • >=<= 的顺序不能反,不能写成 =>=<
  • 可以用数学课学的符号对比:≥ 和 ≤
  • 强调:>= 是“大于或等于“,不是“大于然后等于“

3. 字符串比较

# 字符串按字母顺序比较
print("apple" < "banana")  # 输出:True(a < b)
print("cat" > "dog")       # 输出:False(c < d)

# 数字字符串也可以比较(按字典序)
print("10" < "2")  # 输出:True('1' < '2')
print("9" > "10")  # 输出:True('9' > '1')

# 如果要比数值大小,先转换
print(int("10") > int("2"))  # 输出:True

给家长的小贴士

  • 字符串比较是按“字典序“(字母表顺序)
  • 数字字符串比较可能不符合直觉(“10” < “2” 因为 ‘1’ < ‘2’)
  • 这是个进阶话题,孩子暂时不理解也没关系

4. 比较运算符总结

运算符名称示例结果
==等于5 == 5True
!=不等于5 != 3True
>大于5 > 3True
<小于5 < 10True
>=大于等于5 >= 5True
<=小于等于5 <= 5True

实践 1:数字比较器

让我们用比较运算符做一个数字比较器:

# 数字比较器
print("=" * 35)
print("  数字比较器  ")
print("=" * 35)
print()

# 获取输入
num1 = input("请输入第一个数字:")
num2 = input("请输入第二个数字:")

# 转换成数值
num1 = float(num1)
num2 = float(num2)

# 比较并输出
print()
print("比较结果:")
print("-" * 30)
print(f"{num1} == {num2} : {num1 == num2}")
print(f"{num1} != {num2} : {num1 != num2}")
print(f"{num1} >  {num2} : {num1 > num2}")
print(f"{num1} <  {num2} : {num1 < num2}")
print(f"{num1} >= {num2} : {num1 >= num2}")
print(f"{num1} <= {num2} : {num1 <= num2}")
print("-" * 30)

运行示例:

===================================
  数字比较器
===================================

请输入第一个数字:5
请输入第二个数字:3

比较结果:
------------------------------
5.0 == 3.0 : False
5.0 != 3.0 : True
5.0 >  3.0 : True
5.0 <  3.0 : False
5.0 >= 3.0 : True
5.0 <= 3.0 : False
------------------------------

练习 1:猜数字

编写一个程序:

  1. 设定一个秘密数字(如 7)
  2. 询问用户猜的数字
  3. 告诉用户猜对了还是猜错了
📝 点击查看参考答案
# 猜数字游戏
secret_number = 7

print("猜数字游戏")
print("=" * 25)
print()

# 获取猜测
guess = input("请猜一个数字(1-10):")
guess = int(guess)

# 比较
print()
if guess == secret_number:
    print("🎉 恭喜你,猜对了!")
else:
    print(f"❌ 猜错了,秘密数字是 {secret_number}")

运行示例:

猜数字游戏
=========================

请猜一个数字(1-10):5

❌ 猜错了,秘密数字是 7

给家长的小贴士

  • 这个练习用到了条件语句(第7章内容)
  • 如果孩子还没学,可以简化:直接输出比较结果
  • 可以扩展:给3次机会,提示“大了“或“小了“

逻辑运算符

逻辑运算符用来组合多个布尔值,得到一个新的布尔值。

1. 与运算(and)

and 运算:只有两个都为 True,结果才为 True。

# 两个都为 True
print(True and True)   # 输出:True

# 只要有一个为 False
print(True and False)  # 输出:False
print(False and True)   # 输出:False
print(False and False)  # 输出:False

# 实际例子
age = 12
has_ticket = True

can_enter = age >= 10 and has_ticket
print(can_enter)  # 输出:True(年龄≥10且有票)

类比and 就像“同时满足两个条件“:

  • 你需要同时有门票且年龄≥10才能进入
  • 你需要同时完成作业且打扫房间才能玩游戏

给家长的小贴士

  • 可以用“两个条件都要满足“来解释
  • 举生活中的例子:既要完成作业,又要打扫房间,才能看电视
  • 可以画“真值表“帮助孩子理解,但不要深究

2. 或运算(or)

or 运算:只要有一个为 True,结果就为 True。

# 只要有一个为 True
print(True or True)    # 输出:True
print(True or False)   # 输出:True
print(False or True)    # 输出:True

# 两个都为 False
print(False or False)   # 输出:False

# 实际例子
is_weekend = True
is_holiday = False

can_play = is_weekend or is_holiday
print(can_play)  # 输出:True(周末或假期可以玩)

类比or 就像“满足任意一个条件“:

  • 周末或假期可以玩游戏
  • 带雨伞或雨衣都可以

给家长的小贴士

  • 可以用“至少满足一个条件“来解释
  • 举生活中的例子:带雨伞或雨衣都可以(有一个就行)
  • and 对比:and 是“都要“,or 是“任一“

3. 非运算(not)

not 运算:把 True 变成 False,把 False 变成 True。

print(not True)   # 输出:False
print(not False)  # 输出:True

# 实际例子
is_raining = False
need_umbrella = not is_raining
print(need_umbrella)  # 输出:True(不下雨需要带伞?逻辑需要调整)

# 更合理的例子
is_sunny = True
need_umbrella = not is_sunny
print(need_umbrella)  # 输出:False(晴天不需要带伞)

给家长的小贴士

  • not 就是“取反“、“相反”
  • 可以用“不是“来解释
  • 举生活中的例子:如果你不饿,就去学习

4. 逻辑运算符的优先级

逻辑运算符也有优先级:not > and > or

# not 优先级最高
print(not True and False)  # 输出:False(先算 not True)
print(not (True and False))  # 输出:True(先算括号里的)

# and 比 or 优先级高
print(True or False and False)  # 输出:True(先算 and)

# 使用括号明确优先级
print((True or False) and False)  # 输出:False

给家长的小贴士

  • 建议孩子多使用括号,即使不是必须的
  • 如果不确定优先级,就用括号明确表示
  • 暂时不需要记忆优先级,多写代码自然就熟悉了

5. 逻辑运算符总结

运算符名称规则示例
and两个都为 TrueTrue and True = True
or至少一个为 TrueFalse or True = True
not取反not True = False

实践 2:登录检查器

让我们用逻辑运算符做一个登录检查器:

# 登录检查器
print("=" * 35)
print("  登录检查器  ")
print("=" * 35)
print()

# 设定正确的用户名和密码
correct_username = "xiaoming"
correct_password = "123456"

# 获取输入
username = input("请输入用户名:")
password = input("请输入密码:")

# 检查
print()
username_correct = username == correct_username
password_correct = password == correct_password
can_login = username_correct and password_correct

# 输出
print("检查结果:")
print("-" * 30)
print(f"用户名正确:{username_correct}")
print(f"密码正确:{password_correct}")
print("-" * 30)

if can_login:
    print("✅ 登录成功!")
else:
    print("❌ 登录失败,请检查用户名或密码")

运行示例:

===================================
  登录检查器
===================================

请输入用户名:xiaoming
请输入密码:123

检查结果:
------------------------------
用户名正确:True
密码正确:False
------------------------------
❌ 登录失败,请检查用户名或密码

给家长的小贴士

  • 这里用 and 因为用户名和密码都要正确
  • 可以和孩子讨论:如果用 or 会怎样?(只要对一个就能登录,不安全)
  • 可以扩展:添加账号锁定功能(3次错误后锁定)

练习 2:折扣检查器

编写一个程序:

  1. 询问用户是否为会员(yes/no)
  2. 询问消费金额
  3. 判断是否享受折扣:
    • 会员且消费超过100元,享受8折
    • 非会员且消费超过200元,享受9折
📝 点击查看参考答案
# 折扣检查器
print("=" * 30)
print("  折扣检查器  ")
print("=" * 30)
print()

# 获取输入
member = input("你是会员吗?(yes/no):")
amount = input("请输入消费金额:")

# 转换
amount = float(amount)
is_member = member == "yes" or member == "y" or member == "是"

# 判断折扣
discount = 0
has_discount = False

# 会员消费超过100元,8折
if is_member and amount > 100:
    discount = 0.2
    has_discount = True
    reason = "会员且消费超过100元"

# 非会员消费超过200元,9折
elif not is_member and amount > 200:
    discount = 0.1
    has_discount = True
    reason = "消费超过200元"

# 输出
print()
print(f"会员:{is_member}")
print(f"消费金额:¥{amount:.2f}")
print()

if has_discount:
    final_price = amount * (1 - discount)
    print(f"✅ 享受折扣:{reason}")
    print(f"折扣:{discount * 10:.0f}折")
    print(f"原价:¥{amount:.2f}")
    print(f"最终:¥{final_price:.2f}")
else:
    print("❌ 不享受折扣")
    print(f"应付金额:¥{amount:.2f}")

运行示例:

==============================
  折扣检查器
==============================

你是会员吗?(yes/no):yes
请输入消费金额:150

会员:True
消费金额:¥150.00

✅ 享受折扣:会员且消费超过100元
折扣:8折
原价:¥150.00
最终:¥120.00

给家长的小贴士

  • 这个练习综合运用了 andnot
  • member == "yes" or member == "y" or member == "是" 让用户可以输入多种形式
  • 可以和孩子讨论:为什么会员的要求更宽松?(鼓励成为会员)

布尔值的实际应用

1. 验证输入

# 验证年龄输入
age = input("请输入你的年龄:")
age = int(age)

is_valid = age >= 0 and age <= 120

if is_valid:
    print(f"你的年龄是 {age} 岁")
else:
    print("❌ 年龄不合法!")

2. 检查范围

# 检查温度是否舒适
temperature = 25

is_comfortable = temperature >= 18 and temperature <= 28
print(f"温度舒适:{is_comfortable}")

# 更简洁的写法(数学课学过的区间表示)
is_comfortable = 18 <= temperature <= 28
print(f"温度舒适:{is_comfortable}")

给家长的小贴士

  • 18 <= temperature <= 28 是Python的特殊写法,数学上很直观
  • 其他编程语言可能需要写成 temperature >= 18 and temperature <= 28

3. 闰年判断

判断闰年的规则:

  1. 能被4整除,但不能被100整除;或者
  2. 能被400整除
# 闰年判断器
year = input("请输入年份:")
year = int(year)

# 闰年规则
is_leap = (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)

print()
print(f"{year}年是闰年吗?{is_leap}")

if is_leap:
    print("这一年有366天(2月有29天)")
else:
    print("这一年有365天(2月有28天)")

运行示例:

请输入年份:2024

2024年是闰年吗?True
这一年有366天(2月有29天)

给家长的小贴士

  • 闰年规则比较复杂,可以和孩子一起查资料
  • % 是求余数,year % 4 == 0 表示能被4整除
  • 这个练习综合运用了 andornot

布尔值和其他类型的转换

1. 其他类型转布尔值

在Python中,任何值都可以转换成布尔值:

  • 大多数值都是 True
  • 只有少数“空“值是 False
# 数值转布尔值
print(bool(1))     # 输出:True
print(bool(0))     # 输出:False
print(bool(-5))    # 输出:True
print(bool(3.14))  # 输出:True

# 字符串转布尔值
print(bool("hello"))  # 输出:True
print(bool(""))       # 输出:False(空字符串)

# None 转布尔值
print(bool(None))  # 输出:False

规则总结

  • False 值0, 0.0, "", None
  • True 值:其他所有值

给家长的小贴士

  • 这是个进阶话题,孩子暂时不理解也没关系
  • 可以简单记忆:有东西就是 True,没东西就是 False
  • 暂时不需要深入,知道有这个转换即可

2. 布尔值转其他类型

# 布尔值转数值
print(int(True))   # 输出:1
print(int(False))  # 输出:0

print(float(True))   # 输出:1.0
print(float(False))  # 输出:0.0

# 布尔值转字符串
print(str(True))   # 输出:"True"
print(str(False))  # 输出:"False"

给家长的小贴士

  • True 相当于 1False 相当于 0
  • 这个特性可以用于计算,但初学者不建议这样做
  • 例如:True + True + False = 2(但不要这样写,太容易混淆)

常见错误和调试

错误 1:使用 = 而不是 ==

# 错误示例
age = 10
if age = 10:  # 错误!应该用 ==
    print("你是10岁")

# 正确做法
age = 10
if age == 10:  # 正确
    print("你是10岁")

错误 2:字符串大小写

# 错误示例
name = input("请输入你的名字:")
if name == "xiaoming":  # 如果用户输入 "Xiaoming" 就会出错
    print("欢迎小明")

# 正确做法
name = input("请输入你的名字:")
if name.lower() == "xiaoming":  # 统一转成小写比较
    print("欢迎小明")

错误 3:比较数字字符串

# 错误示例
age = input("请输入年龄:")
if age > 10:  # 错误!这是字符串比较,不是数值比较
    print("你超过10岁了")

# 正确做法
age = input("请输入年龄:")
age = int(age)  # 先转换成数值
if age > 10:
    print("你超过10岁了")

给家长的小贴士

  • 这些错误是初学者最容易犯的
  • 建议让孩子亲自运行这些错误代码,观察错误信息
  • Python的错误信息通常很清楚,学会看错误信息很重要

章节小结

恭喜你完成了这一章!让我们回顾一下学到的内容:

核心概念

  1. 布尔值(bool):只有两个值,True(真)和 False(假)
  2. 比较运算符==, !=, >, <, >=, <=
  3. 逻辑运算符and(与),or(或),not(非)

重要规则

  • = 是赋值,== 是比较
  • and:两个都为 True,结果才为 True
  • or:只要有一个为 True,结果就为 True
  • not:取反,True 变 False,False 变 True
  • 优先级:not > and > or

实际应用

  • 比较数字大小
  • 验证输入是否合法
  • 检查多个条件
  • 闰年判断等复杂逻辑

给家长的小贴士:教学建议

教学重点

  1. True 和 False:理解布尔值只有两个值
  2. 比较运算符:特别是 === 的区别
  3. 逻辑运算符:用生活中的例子类比
  4. 实际应用:让孩子看到布尔值的用途

常见问题及解答

  1. 问:为什么 True 的首字母要大写?

    • 答:这是Python的规定。就像人名首字母要大写一样,True 和 False 是Python的“名字“,首字母必须大写。
  2. 问:=== 有什么区别?

    • 答:= 是“赋值“,把东西放进盒子;== 是“比较“,问两个东西相等吗?
  3. 问:什么时候用 and,什么时候用 or

    • 答:需要“两个条件都满足“用 and,需要“至少满足一个“用 or
  4. 问:not 是什么意思?

    • 答:not 就是“不是“、“相反”。not True 就是“不是True“,也就是 False。

练习建议

  • 让孩子用布尔值判断各种条件:年龄、成绩、温度等
  • 多用生活中的例子:登录验证、折扣检查、游戏规则
  • 鼓励孩子设计自己的“规则“并用布尔值表达

挑战练习

  1. 石头剪刀布:编写程序,判断两个输入谁赢(需要考虑所有情况)

  2. 成绩等级:输入分数,判断等级(A: 90-100, B: 80-89, C: 70-79, D: 60-69, F: 0-59)

  3. 三角形判断:输入三条边的长度,判断能否组成三角形(任意两边之和大于第三边)

  4. 密码强度检查:检查密码是否满足:至少8个字符、包含大写字母、包含小写字母、包含数字

🔗 下一章:顺序语句

在下一章中,我们将学习:

  • 什么是程序的三种基本结构
  • 顺序执行的概念
  • 如何让程序按顺序执行语句
  • 使用 turtle 库绘制图形

布尔值帮我们做判断,顺序语句帮我们组织代码。两者结合起来,我们就可以编写更复杂的程序了!

顺序语句与第一个图形程序

引言

在前面的章节中,我们学习了变量、数据类型和基本的输入输出。现在,让我们开始学习程序的控制流程——也就是程序执行的顺序。

想象一下,你正在按照菜谱做一道菜:

  1. 先准备食材
  2. 然后切菜
  3. 接着炒菜
  4. 最后装盘

这些步骤必须按照顺序执行,不能跳过或颠倒。Python程序也是如此,代码默认从上到下、从左到右依次执行,这就是顺序语句

你知道吗? 🤔 计算机的**CPU(中央处理器)**就是这样工作的!它会一条一条地读取指令,按照顺序执行。就像你按照菜谱做菜一样,CPU也是严格按照代码的顺序来工作的。

这一章,我们将:

  • 理解程序的执行顺序
  • 了解CPU是如何执行指令的
  • 学习什么是“库“和“函数“
  • 使用turtle库画出第一个图形
  • 学习坐标系的概念
  • 复习几何知识(正方形、长方形、三角形的性质)
  • 用代码创作各种有趣的图形

什么是顺序语句

代码执行顺序

在Python中,代码默认按照从上到下的顺序执行,一行一行地运行。

# 示例:按顺序执行的代码
print("第一步:起床")
print("第二步:刷牙")
print("第三步:吃早餐")
print("第四步:上学")

输出结果:

第一步:起床
第二步:刷牙
第三步:吃早餐
第四步:上学

如果改变顺序,结果也会改变:

# 改变顺序
print("第四步:上学")
print("第一步:起床")
print("第三步:吃早餐")
print("第二步:刷牙")

输出结果:

第四步:上学
第一步:起床
第三步:吃早餐
第二步:刷牙

👨‍🏫 给家长的Tips

教学方法建议:

  • 可以用生活中的例子帮助孩子理解顺序的概念,比如:
    • 穿衣服的顺序(内裤→裤子→外套)
    • 做作业的步骤(打开书包→拿出作业→开始写作业)
    • 刷牙的步骤(挤牙膏→刷牙→漱口)
  • 重要提示:强调计算机很“笨“,完全按照我们写的代码顺序执行,不会自动调整
  • 可以让孩子想一些生活中的例子,来说明顺序的重要性

常见问题解答:

  • :为什么不能让计算机自己判断顺序?
  • :计算机没有“常识“,它只能严格按照我们写的代码执行。这就像一个只会按字面意思执行的机器人

为什么顺序很重要

顺序错误可能导致程序出错或结果不正确。让我们看一个例子:

# 正确的顺序:先赋值,再使用
age = 10
print(f"我的年龄是{age}岁")

# 错误的顺序:先使用,后赋值(会报错)
# print(f"我的身高是{height}厘米")  # 变量还不存在
# height = 150

练习1:下面的代码输出什么?想一想为什么。

a = 5
b = 10
a = b
b = a
print(a, b)
👉 点击查看答案

答案10 10

解释

  1. a = 5:a的值是5
  2. b = 10:b的值是10
  3. a = b:把b的值(10)赋给a,现在a是10
  4. b = a:把a的值(10)赋给b,b还是10
  5. 最终输出 10 10

如果想交换两个变量的值,需要用第三个变量:

a = 5
b = 10
temp = a  # 先保存a的值
a = b     # 把b给a
b = temp  # 把原来的a给b
print(a, b)  # 输出:10 5

CPU是如何执行指令的 💡

你知道吗? 当你运行Python程序时,计算机的**CPU(中央处理器)**正在做以下工作:

  1. 取指令:CPU从内存中读取一行代码
  2. 译码:CPU理解这行代码要做什么
  3. 执行:CPU执行这行代码
  4. 重复:然后读取下一行代码,继续执行

这个过程就像你按菜谱做菜:

  • 看菜谱上的一行指令(取指令)
  • 理解这行指令的意思(译码)
  • 按照指令去做(执行)
  • 然后看下一行指令(重复)

比喻时间 🎯 想象CPU是一个非常勤奋的厨师:

  • 代码就是菜谱
  • CPU就是厨师
  • 厨师严格按照菜谱的顺序,一行一行地做菜
  • 如果菜谱写错了,厨师也会照做(因为厨师不会自己思考)

数学小知识 📐

  • 如果一个CPU每秒能执行10亿次运算(1GHz)
  • 执行100行简单的代码,只需要0.0000001秒!
  • 这就是为什么计算机这么快的原因!

👨‍🏫 给家长的Tips

知识点补充(针对家长):

  • CPU是计算机的“大脑“,负责执行所有的计算和控制
  • CPU的主频(如2GHz、3GHz)表示每秒能执行多少个时钟周期
  • 1GHz = 10亿次/秒,3GHz = 30亿次/秒
  • 现代CPU非常复杂,但基本原理就是“取指-译码-执行“的循环

如何给孩子讲解:

  • 不需要讲太多技术细节
  • 重点让孩子理解:CPU是按顺序执行代码的
  • 可以用“机器人厨师“的比喻来帮助理解
  • 强调:代码的顺序非常重要,因为CPU完全按顺序执行

库和函数的概念

什么是库

Python非常强大,但有些功能不是Python自带的,需要我们额外引入。这些额外的功能集合叫做(library)或模块(module)。

就像是一个工具箱:

  • Python自带了一些基本工具(比如print、input)
  • 但如果我们需要特殊工具(比如画图、做数学运算),就需要引入专门的工具箱

什么是函数

函数(function)就像是一个“快捷指令“或“小机器“:

  • 你给它一些材料(参数)
  • 它内部进行处理
  • 最后返回结果

比如,我们一直在用的print()input()都是函数:

  • print("Hello"):给print函数一个字符串,它负责显示在屏幕上
  • input("姓名:"):给input函数一个提示语,它负责接收用户输入

引入库的方法

使用import语句引入库:

# 引入整个库
import turtle

# 使用库中的函数:库名.函数名()
turtle.forward(100)

或者给库起个简短的名字:

# 引入库并给它起个简短的名字
import turtle as t

# 使用时更简短
t.forward(100)

认识Turtle库

什么是Turtle库

Turtle(海龟)是Python自带的一个画图库,特别适合初学者。它的原理很简单:

  • 屏幕上有一只“小海龟“
  • 我们可以用代码控制它移动、转向
  • 小海龟爬过的轨迹会留下线条

这种方式画图非常直观,能帮助孩子们理解坐标系角度等数学概念。

给家长的小贴士

  • Turtle库是1960年代为儿童学习编程设计的,历史悠久且效果很好
  • 它能将抽象的编程概念可视化
  • 建议家长和孩子一起运行代码,观察效果

安装Turtle库

Turtle库通常是Python自带的,不需要额外安装。但如果遇到问题,可以用以下方法安装:

# 在命令行中运行
pip install turtle

坐标系的概念

在使用Turtle画图前,我们需要了解坐标系

      y轴(向上为正)
      ↑
      |
      |
      |
      •------→ x轴(向右为正)
     (0,0)
  • 屏幕中心是原点 (0, 0)
  • 向右是x轴正方向(正数)
  • 向上是y轴正方向(正数)
  • 向左是x轴负方向(负数)
  • 向下是y轴负方向(负数)

Turtle的基本命令

让我们学习Turtle的基本命令:

1. 移动命令

import turtle

# 创建一只海龟
t = turtle.Turtle()

# 前进(画线)
t.forward(100)    # 向前移动100像素
t.fd(100)        # forward的简写

# 后退(画线)
t.backward(50)   # 向后移动50像素
t.bk(50)         # backward的简写

# 保持窗口打开
turtle.done()

效果:海龟向前画一条100像素的线,然后向后退50像素。

2. 转向命令

import turtle

t = turtle.Turtle()

# 左转
t.left(90)       # 左转90度
t.lt(90)         # left的简写

# 右转
t.right(90)      # 右转90度
t.rt(90)         # right的简写

turtle.done()

注意

  • left(90)表示左转90度
  • 转向时海龟停留在原地,只是改变了朝向
  • 转向后继续移动就会朝新的方向前进

给家长的小贴士

  • 可以用实物演示:让孩子站起来,模拟海龟的移动和转向
  • 强调“转向“和“移动“是两个独立的动作
  • 介绍角度概念:90度是直角,180度是掉头

3. 画笔控制

import turtle

t = turtle.Turtle()

# 抬起画笔(移动不留痕迹)
t.penup()
t.goto(50, 50)  # 移动到(50, 50),不画线

# 放下画笔(移动会画线)
t.pendown()
t.goto(100, 100)  # 移动到(100, 100),会画线

# 简写
t.up()    # 抬笔
t.down()  # 落笔

turtle.done()

4. 颜色和填充

import turtle

t = turtle.Turtle()

# 设置画笔颜色
t.pencolor("red")  # 设置为红色
t.forward(100)

# 设置填充颜色
t.fillcolor("blue")  # 填充色为蓝色

# 开始填充
t.begin_fill()

# 画一个正方形(用顺序语句,每条边都写出来)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)

# 结束填充
t.end_fill()

turtle.done()

绘制基本图形

1. 画正方形 🟥

先复习数学知识! 📚

在数学课上,我们学过正方形的性质:

  • ✅ 有4条边
  • ✅ 每条边的长度都相等
  • ✅ 有4个角,每个角都是直角(90度)
  • ✅ 内角和 = 4 × 90° = 360度

现在让我们用代码画一个正方形:

import turtle

t = turtle.Turtle()

# 画正方形 - 第1条边
t.forward(100)  # 边长100像素
t.left(90)      # 左转90度

# 第2条边
t.forward(100)
t.left(90)

# 第3条边
t.forward(100)
t.left(90)

# 第4条边
t.forward(100)
t.left(90)  # 画完后转回原方向

turtle.done()

效果:一个边长为100像素的正方形。

想一想 🤔

  • 为什么要写4次forward(100)
    • 因为正方形有4条边
  • 为什么要写4次left(90)
    • 因为每次要转90度(360° ÷ 4 = 90°)

数学小挑战 📐

  • 如果边长改为200,代码怎么改?
    • 把所有的forward(100)改成forward(200)
  • 如果要画正六边形(6条边),每次转多少度?
    • 360° ÷ 6 = 60度

👨‍🏫 给家长的Tips

教学方法建议:

  • 先让孩子在纸上手算:画正方形需要写多少行代码?
    • 4条边,每条边需要2行代码(forward + left)
    • 所以需要 4 × 2 = 8行代码
  • 引导孩子思考:
    • “如果画正十边形,需要写多少行代码?” (10 × 2 = 20行)
    • “如果画正一百边形,要写多少行代码?” (100 × 2 = 200行!)
  • 告诉孩子:“这样重复写代码很麻烦,下一章我们会学习更简洁的方法!”

数学知识融入:

  • 正方形的性质:4边相等,4角直角
  • 多边形外角和公式:每次转向角度 = 360° ÷ 边数
  • 这个公式适用于所有正多边形

2. 观察代码的重复规律

上面的代码重复了4次相同的操作(forward和left)。你可能会想:有没有更简单的方法呢?

答案是有的!在第8章《循环语句》中,我们将学习循环,可以让我们用更少的代码完成相同的任务。

👨‍🏫 给家长的Tips

这是一个很好的教学时机!

  • 先让孩子用顺序语句完成图形绘制,理解每一步在做什么
  • 引导孩子观察代码的重复规律
  • 告诉孩子:“你看,这些代码重复了4次,很相似对吧?下一章我们要学一种更聪明的方法!”
  • 这样在学循环时,孩子会有强烈的期待感,理解为什么要学循环

3. 画长方形 ▭

复习数学知识! 📚

在数学课上,我们学过长方形的性质:

  • ✅ 有4条边
  • 对边相等(上边=下边,左边=右边)
  • ✅ 有4个角,每个角都是直角(90度)
  • ✅ 内角和 = 360度

现在让我们用代码画一个长方形:

import turtle

t = turtle.Turtle()

# 设置长和宽
length = 150  # 长
width = 80    # 宽

# 画长方形 - 第1条边(长边)
t.forward(length)   # 向前150像素
t.left(90)          # 左转90度

# 第2条边(宽边)
t.forward(width)    # 向前80像素
t.left(90)          # 左转90度

# 第3条边(长边)
t.forward(length)   # 向前150像素
t.left(90)          # 左转90度

# 第4条边(宽边)
t.forward(width)    # 向前80像素
t.left(90)          # 左转90度

turtle.done()

效果:一个长150像素、宽80像素的长方形。

想一想 🤔

  • 为什么长边和宽边的数值要交替出现?
    • 因为长方形是对边相等!
  • 如果要画正方形,应该怎么设置length和width?
    • 把length和width设为相同的值!

数学小练习 📐

  • 长方形的周长 = (长 + 宽) × 2
  • 上面的长方形周长 = (150 + 80) × 2 = 460像素
  • 你能算出如果length=200, width=50,周长是多少吗?
    • (200 + 50) × 2 = 500像素

练习2:修改上面的代码,画一个长200、宽50的长方形。

👉 点击查看答案
import turtle

t = turtle.Turtle()

length = 200
width = 50

# 用顺序语句画出长方形(4条边)
t.forward(length)
t.left(90)
t.forward(width)
t.left(90)
t.forward(length)
t.left(90)
t.forward(width)
t.left(90)

turtle.done()

思考:上面的代码也重复了(forward, left, forward, left)两次。在学完循环后,你可以尝试用循环简化这段代码!

👨‍🏫 给家长的Tips

教学方法建议:

  • 对比正方形和长方形的代码:
    • 正方形:4次forward(100) - 数值相同
    • 长方形:forward(150)和forward(80)交替出现 - 数值不同
  • 引导孩子理解“对边相等“的概念
  • 可以让孩子计算:
    • 画长方形需要多少行代码?(8行)
    • 如果画20个长方形,要写多少行代码?(20 × 8 = 160行!)
  • 告诉孩子:“下一章学了循环,3行代码就能解决重复问题!”

4. 画三角形 △

复习数学知识! 📚

在数学课上,我们学过等边三角形(正三角形)的性质:

  • ✅ 有3条边
  • ✅ 每条边的长度都相等
  • ✅ 有3个角,每个角都是60度
  • ✅ 内角和 = 3 × 60° = 180度(所有三角形内角和都是180度!)

现在让我们用代码画一个等边三角形:

import turtle

t = turtle.Turtle()

# 等边三角形
side = 100  # 边长

# 第1条边
t.forward(side)
t.left(120)  # 左转120度

# 第2条边
t.forward(side)
t.left(120)

# 第3条边
t.forward(side)
t.left(120)

turtle.done()

效果:一个边长为100像素的等边三角形。

重要问题 🤔

  • 三角形的内角是60度,为什么代码里写的是left(120)
  • 答案:因为海龟转的是外角,不是内角!
  • 外角 = 180° - 内角 = 180° - 60° = 120°

更简单的计算方法 📐

  • 对于任意正n边形,每次转向角度 = 360° ÷ n
  • 正三角形:360° ÷ 3 = 120°
  • 正方形:360° ÷ 4 = 90°
  • 正五边形:360° ÷ 5 = 72°

数学小挑战 📐

  • 三角形的周长 = 100 × 3 = 300像素
  • 三角形的内角和 = 60° + 60° + 60° = 180度
  • 所有三角形的内角和都是180度,你记住了吗?

👨‍🏫 给家长的Tips

知识点补充(针对家长):

  • 外角的概念:延长线与边的夹角
  • 为什么要转外角:因为海龟是“转弯“,不是“掉头“
  • 外角公式:360° ÷ 边数 (适用于所有正多边形)

如何给孩子讲解:

  • 可以让孩子站起来,模拟海龟走路:
    • 向前走100步
    • 原地左转(不是掉头,是转弯)
    • 想象你要“转弯多少度“才能走向下一个方向
  • 也可以用“走路转弯“的生活例子来帮助理解

练习3:计算五边形每次应该转多少度?

👉 点击查看答案

答案:360 ÷ 5 = 72度

代码

import turtle

t = turtle.Turtle()

side = 100
angle = 360 / 5  # 72度

# 用顺序语句画出五边形(5条边)
t.forward(side)
t.left(angle)
t.forward(side)
t.left(angle)
t.forward(side)
t.left(angle)
t.forward(side)
t.left(angle)
t.forward(side)
t.left(angle)

turtle.done()

思考:五边形需要重复5次相同操作。想象一下,如果要画10边形或20边形,代码会变得很长。在学习循环后,你就可以用短短几行代码完成!

5. 画圆形

import turtle

t = turtle.Turtle()

# 方法1:使用circle命令
t.circle(50)  # 画半径为50的圆

# 移动位置
t.penup()
t.goto(100, 0)
t.pendown()

# 方法2:通过画多边形近似圆
t.circle(80, steps=100)  # 用100条线段近似圆

turtle.done()

实践项目:彩色多边形艺术

项目1:多彩正方形

让我们画一个填充颜色的正方形:

import turtle

t = turtle.Turtle()

# 设置画笔和填充颜色
t.pencolor("blue")      # 边框蓝色
t.fillcolor("yellow")   # 填充黄色

# 开始填充
t.begin_fill()

# 画正方形(用顺序语句)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)

# 结束填充
t.end_fill()

# 写上文字
t.penup()
t.goto(0, -20)
t.write("正方形", align="center", font=("Arial", 16, "bold"))

turtle.done()

项目2:彩色圆圈

import turtle

t = turtle.Turtle()

# 画第1个圆(红色,半径20)
t.penup()
t.goto(0, 0)
t.pendown()
t.pencolor("red")
t.circle(20)

# 画第2个圆(橙色,半径40)
t.penup()
t.goto(0, -20)
t.pendown()
t.pencolor("orange")
t.circle(40)

# 画第3个圆(黄色,半径60)
t.penup()
t.goto(0, -40)
t.pendown()
t.pencolor("yellow")
t.circle(60)

turtle.done()

给家长的Tips

  • 这个项目画了3个圆圈,每个圆圈的位置和大小都不同
  • 引导孩子观察:每个圆圈的代码都很相似,只是颜色和数字不同
  • 告诉孩子:“如果我们想画10个圆圈,就要写30行类似的代码。学完循环后,3行代码就能搞定!”
  • 这为学习循环埋下伏笔

练习:尝试在上面的代码基础上,继续添加第4个、第5个圆圈,让它们越来越大!

项目3:星星(挑战题)

五角星的每个角是36度,我们来画一个五角星:

import turtle

t = turtle.Turtle()

# 设置画笔颜色
t.pencolor("gold")
t.fillcolor("orange")

# 移动到合适的位置
t.penup()
t.goto(0, 100)
t.pendown()

# 开始填充
t.begin_fill()

# 画五角星(用顺序语句,5条边)
t.forward(200)
t.right(144)
t.forward(200)
t.right(144)
t.forward(200)
t.right(144)
t.forward(200)
t.right(144)
t.forward(200)
t.right(144)

# 结束填充
t.end_fill()

turtle.done()

给家长的小贴士

  • 五角星有5个角,每个角36度
  • 每次转向的是外角:180 - 36 = 144度
  • 可以让孩子先在纸上画五角星,数数每次转多少度

Turtle常用函数速查表

给孩子打印一张函数表,方便他们查找使用:

移动命令

函数简写说明
forward(距离)fd(距离)向前移动指定距离
backward(距离)bk(距离)back(距离)
goto(x, y)移动到指定坐标
setx(x)设置x坐标
sety(y)设置y坐标

转向命令

函数简写说明
left(角度)lt(角度)左转指定角度
right(角度)rt(角度)右转指定角度
setheading(角度)seth(角度)设置朝向(0=东,90=北,180=西,270=南)

画笔控制

函数简写说明
penup()up()pu()
pendown()down()pd()
pensize(宽度)width(宽度)设置画笔粗细
pencolor(颜色)设置画笔颜色
fillcolor(颜色)设置填充颜色
begin_fill()开始填充
end_fill()结束填充

其他命令

函数说明
circle(半径)画圆
dot(大小)画点
write(文字)写文字
clear()清除画布
reset()重置画布和海龟
hideturtle()隐藏海龟
showturtle()显示海龟
speed(速度)设置速度(1-10,0最快)

综合练习

练习1:画房子

用Turtle画一个简单的房子:

import turtle

t = turtle.Turtle()
t.speed(5)  # 设置速度

# 房子主体(正方形)
t.fillcolor("yellow")
t.begin_fill()
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.end_fill()

# 房顶(三角形)
t.penup()
t.goto(0, 100)
t.pendown()

t.fillcolor("red")
t.begin_fill()
t.forward(100)
t.left(120)
t.forward(100)
t.left(120)
t.forward(100)
t.left(120)
t.end_fill()

# 门(长方形)
t.penup()
t.goto(30, -50)
t.pendown()

t.fillcolor("brown")
t.begin_fill()
t.forward(40)
t.left(90)
t.forward(60)
t.left(90)
t.forward(40)
t.left(90)
t.forward(60)
t.left(90)
t.end_fill()

turtle.done()

练习2:画花朵

这个练习使用循环来画多个花瓣。由于我们还没学循环,这个练习我们先跳过,等学完第8章循环语句后,再回来完成它!

作为替代,你可以尝试:

  • 画一个简单的花朵(用多个圆形组成)
  • 画一棵树(用长方形做树干,三角形或圆形做树冠)
  • 或者发挥创意,画你自己想画的任何东西!

👨‍🏫 给家长的Tips

这个画花朵的练习需要使用循环来重复画花瓣。可以:

  • 告诉孩子:“这个练习需要重复画6次花瓣,每次都要写很多相似的代码,有点复杂”
  • “我们先学习更简单的图形,等学完循环后,再用短短几行代码完成这个美丽的花朵!”
  • 保持孩子的学习兴趣和期待感

练习3:创意绘画

让孩子自由发挥,创作自己的作品!

提示

  • 可以画动物、植物、建筑等
  • 尝试使用不同的颜色和形状
  • 组合多个图形形成复杂图案

调试技巧

常见问题

  1. 窗口一闪而过

    # 在程序最后加上
    turtle.done()
    
  2. 画图速度太慢

    t = turtle.Turtle()
    t.speed(0)  # 0是最快速度,10是快,1是慢
    
  3. 找不到海龟

    t.showturtle()  # 显示海龟
    
  4. 颜色不生效

    # 确保颜色名称正确(英文)
    t.pencolor("red")  # 正确
    # t.pencolor("红色")  # 错误
    

给家长的小贴士

  • 教孩子学会看错误信息
  • 使用t.speed()设置速度,方便观察画图过程
  • 使用t.clear()清除画布重新开始
  • 鼓励孩子多尝试,不怕出错

章节小结

我们学到了什么

  1. 顺序语句:代码从上到下依次执行
  2. CPU执行原理 💡:
    • CPU按照“取指-译码-执行“的循环工作
    • 代码的顺序非常重要,因为CPU完全按顺序执行
    • 现代CPU每秒可以执行几十亿次运算!
  3. 库的概念:Python的扩展功能集合
  4. 函数的概念:完成特定功能的代码块
  5. import语句:如何引入库
  6. 坐标系:二维平面的定位方法
  7. Turtle库:用代码画图
    • 移动命令:forward, backward, goto
    • 转向命令:left, right
    • 画笔控制:penup, pendown, color
    • 基本图形:正方形、长方形、三角形、圆形
  8. 数学知识 📐:
    • 正方形:4边相等,4角直角,内角和360°
    • 长方形:对边相等,4角直角,内角和360°
    • 等边三角形:3边相等,3角60°,内角和180°
    • 正多边形外角公式:每次转向角度 = 360° ÷ 边数

重要概念回顾

  • 执行顺序:代码按从上到下的顺序执行
  • 函数调用库名.函数名(参数)
  • 坐标系:中心是(0,0),向右x正,向上y正
  • 角度:360度是一圈,90度是直角
  • CPU工作原理:取指→译码→执行→重复

给家长的总结 📋

本章核心知识点:

  1. ✅ 顺序执行的概念(程序控制的基础)
  2. ✅ 计算机硬件基础(CPU的工作原理)
  3. ✅ 几何知识融入(正方形、长方形、三角形的性质)
  4. ✅ Turtle库的基本使用
  5. ✅ 坐标系和角度的概念

教学检查点:

  • 孩子能理解为什么代码的顺序很重要?
  • 孩子能复述CPU是如何执行代码的吗?
  • 孩子能独立用代码画出正方形、长方形、三角形吗?
  • 孩子能计算正n边形每次应该转多少度吗?
  • 孩子对学习循环有期待感吗?(知道顺序语句的局限性)

常见问题与解答:

  • :为什么一定要学顺序语句?直接学循环不行吗? :顺序执行是编程的基础,必须先理解“一步一步执行“,才能理解“重复执行“。这是从具体到抽象的认知过程。
  • :孩子觉得写重复代码很无聊怎么办? :这正是我们想要的效果!让孩子感受到重复代码的麻烦,学循环时才会有强烈的需求感和学习动力。
  • :CPU的内容会不会太深奥? :我们只需要让孩子理解“按顺序执行“的概念即可,不需要深入技术细节。用“厨师按菜谱做菜“的比喻就足够了。

下一步

在下一章(第7章),我们将学习条件语句(if语句),让程序能够根据不同情况做出不同的决定。结合Turtle库,我们就能画出更加智能和有趣的图形!

再下一章(第8章),我们将学习循环语句,到时候你就可以用短短几行代码画出正方形、长方形、三角形和多边形了!是不是很期待?😊

挑战练习

  1. 必做

    • 画一个边长为80的正方形,填充绿色
    • 画一个长150、宽100的长方形,填充蓝色
    • 画一个六边形(💡提示:每次转多少度?)
  2. 选做

    • 画出你的名字首字母
    • 画一个笑脸(圆形脸+眼睛+嘴巴)
    • 画一个彩虹(7个半圆)
  3. 挑战 🌟:

    • 画出自己设计的房子(至少有主体、门窗、屋顶)
    • 创作一个抽象艺术画(使用不同颜色和形状)
    • 数学挑战:计算一下,画一个正100边形需要写多少行代码?如果用循环,可以节省多少行?(答案:100边形需要200行代码,用循环只需要5行!)

加油!你已经能用代码画出美丽的图形了!🎨🐢

预告 📢 下一章学完条件语句,后面我们还要学循环语句。到时候,你就可以用短短几行代码完成:

# 用循环画正方形(第8章会学)
for i in range(4):
    t.forward(100)
    t.left(90)

是不是很简洁?让我们一起期待吧!

条件语句

引言

在前面的章节中,我们学习了顺序语句——程序按从上到下的顺序执行。但现实生活中,很多决定需要根据条件来做不同的选择。

想象一下:

  • 如果下雨,就带伞;如果不下雨,就不带伞
  • 如果温度高于30度,就开空调;否则就开窗
  • 如果作业写完了,就可以玩游戏;否则就继续写作业

这些都需要判断一个条件,然后根据判断结果做不同的事情。在Python中,我们用条件语句来实现这种功能。

你知道吗? 🤔 在上一章,我们学习了CPU如何按顺序执行代码。但CPU不仅能按顺序执行,还能做逻辑判断

  • CPU可以比较两个数字的大小
  • CPU可以判断一个条件是True还是False
  • 根据判断结果,CPU可以决定执行哪一段代码

这就是条件语句的硬件基础!

这一章,我们将学习:

  • 什么是条件语句
  • if语句的基本用法
  • if-else语句(两种选择)
  • if-elif-else语句(多种选择)
  • 嵌套if语句(条件中的条件)
  • 复习数学比较大小的知识
  • 用条件语句解决数学应用题

回顾:布尔值和比较运算

在第5章,我们学习了布尔值(True和False)和比较运算符。它们是条件语句的基础。

数学小知识 📐 在数学课上,我们学过比较两个数的大小:

  • 大于:a > b (a比b大)
  • 小于:a < b (a比b小)
  • 等于:a = b (a和b相等)
  • 大于等于:a ≥ b (a大于或等于b)
  • 小于等于:a ≤ b (a小于或等于b)

Python中的比较运算符和数学中的符号很像,只是写法稍有不同!

比较运算符

让我们快速回顾一下:

# 等于 (数学中是 =,Python中是 ==)
print(5 == 5)   # True
print(5 == 3)   # False

# 不等于 (数学中是 ≠)
print(5 != 3)   # True
print(5 != 5)   # False

# 大于 (和数学一样)
print(10 > 5)   # True
print(5 > 10)   # False

# 小于 (和数学一样)
print(3 < 7)    # True
print(7 < 3)    # False

# 大于等于 (数学中是 ≥)
print(8 >= 8)   # True
print(8 >= 5)   # True

# 小于等于 (数学中是 ≤)
print(6 <= 10)  # True
print(6 <= 6)   # True

数学练习 📝 用Python比较以下数字的大小:

  1. 100 和 200 (答案: 100 < 200,所以100 < 200是True)
  2. 3.14 和 3.14159 (答案: 3.14 < 3.14159)
  3. -5 和 -10 (答案: -5 > -10,负数绝对值越大,数值越小)

注意 ⚠️

  • 在数学中,我们用“=“表示相等
  • 在Python中,“=“是赋值(把值给变量),”==“才是比较(判断是否相等)
  • 这个区别非常重要!

布尔运算符

我们也学过如何组合多个条件:

# and(两个条件都满足)
age = 15
print(age >= 10 and age <= 18)  # True(10到18岁之间)

# or(至少一个条件满足)
day = "周六"
print(day == "周六" or day == "周日")  # True(是周末)

# not(取反)
is_raining = False
print(not is_raining)  # True(不下雨)

数学中的“且“和“或“ 📐 在数学中,我们学过:

  • 且(∧): 两个条件都满足 (对应Python的and)
  • 或(∨): 至少一个条件满足 (对应Python的or)
  • 非(¬): 取反 (对应Python的not)

数学练习 📝

  1. 如果x > 5且x < 10,Python应该写成?

    x > 5 and x < 10
    
  2. 如果x ≤ 0或x ≥ 100,Python应该写成?

    x <= 0 or x >= 100
    

👨‍🏫 给家长的Tips

教学方法建议:

  • 确保孩子理解比较运算符和布尔值
  • 可以用生活中的例子练习:判断年龄、分数、温度等
  • 重点强调:“==“是等于(比较),”=“是赋值(赋值)
  • 可以在纸上画数轴,帮助孩子理解大于、小于的概念

知识点补充(针对家长):

  • CPU有专门的比较指令(如CMP指令),用于比较两个数的大小
  • CPU有逻辑运算指令(如AND、OR、NOT),用于组合条件
  • 这些指令的结果都是布尔值(True/False),在CPU中用1和0表示
  • 条件判断的底层原理:CPU根据比较结果(0或1)决定是否“跳转“到其他代码执行

如何给孩子讲解:

  • 不需要深入CPU指令集的细节
  • 重点让孩子理解:CPU可以比较数字,并根据结果做决定
  • 可以用“如果温度>30度,就开空调“的例子来类比CPU的逻辑判断

if语句:最简单的条件判断

if语句的基本语法

if 条件:
    # 条件为True时执行的代码
    代码块

语法要点

  • if后面跟着一个条件(布尔表达式)
  • 条件后面必须有冒号:(英文冒号)
  • 要执行的代码必须缩进(通常用4个空格或1个Tab)
  • 只有条件为True时,代码块才会执行

示例1:分数奖励

假设小明的Python成绩大于98分,就能获得一个霸王龙玩具:

# 设置分数
score = 99

# 判断分数是否大于98
if score > 98:
    print("恭喜你!")
    print("你获得了一个霸王龙玩具!")

print("程序结束")

输出结果:

恭喜你!
你获得了一个霸王龙玩具!
程序结束

如果分数不满足条件:

# 设置分数
score = 95

# 判断分数是否大于98
if score > 98:
    print("恭喜你!")
    print("你获得了一个霸王龙玩具!")

print("程序结束")

输出结果:

程序结束

可以看到,当条件不满足时,if语句内的代码不会执行,程序直接跳过。

示例2:判断奇数

# 输入一个数字
num = int(input("请输入一个整数:"))

# 判断是否为奇数(除以2余1)
if num % 2 == 1:
    print(f"{num}是奇数")

print("判断结束")

给家长的小贴士

  • 解释什么是“奇数“:不能被2整除的数(除以2余1)
  • 解释%运算符:求余数
  • 让孩子尝试输入不同的数字,观察结果

练习1

题目:小明考试如果及格(分数≥60),爸爸奖励他一辆汽车(输出“gift: car“),如果不及格则没有奖励(输出“gift: none“)。

👉 点击查看答案
# 输入分数
score = int(input("请输入小明的考试分数:"))

# 判断是否及格
if score >= 60:
    print("gift: car")
else:
    print("gift: none")

注意:这个练习需要用到if-else语句,下面马上就会学到!

if-else语句:两种选择

当我们需要在两种情况中做选择时,使用if-else语句。

if-else的基本语法

if 条件:
    # 条件为True时执行
    代码块1
else:
    # 条件为False时执行
    代码块2

示例

  • 如果小明Python考试成绩大于98分,老师就奖励他一个小汽车
  • 否则老师就罚他抄代码100遍
# 输入分数
score = int(input("请输入小明的Python成绩:"))

# 判断分数
if score > 98:
    print("🎉 恭喜你!奖励一个小汽车!")
else:
    print("😢 很遗憾,请抄代码100遍")

print("判断结束")

运行示例1(分数大于98):

请输入小明的Python成绩:100
🎉 恭喜你!奖励一个小汽车!
判断结束

运行示例2(分数不大于98):

请输入小明的Python成绩:95
😢 很遗憾,请抄代码100遍
判断结束

示例:用户登录验证

设定一个用户名和密码,用户输入用户名和密码都正确,打印“welcome“,否则输出“error“。

# 设定正确的用户名和密码
correct_username = "admin"
correct_password = "123456"

# 用户输入
username = input("请输入用户名:")
password = input("请输入密码:")

# 判断用户名和密码是否都正确
if username == correct_username and password == correct_password:
    print("welcome")
else:
    print("error")

给家长的小贴士

  • 解释为什么用and而不是or(两个条件都要满足)
  • 可以让孩子故意输入错误的用户名或密码,测试程序
  • 强调安全:实际程序中密码不会明文存储

示例:判断奇数和偶数

# 输入一个数字
num = int(input("请输入一个整数:"))

# 判断奇偶
if num % 2 == 0:
    print(f"{num}是偶数")
else:
    print(f"{num}是奇数")

练习2

题目:一张桌子5岁的男孩能推动,7岁以上的女孩能推动。根据输入的性别和年龄,判断是否能推动。

👉 点击查看答案
# 输入性别和年龄
gender = input("请输入性别(男/女):")
age = int(input("请输入年龄:"))

# 判断是否能推动(男孩且5岁,或女孩且大于7岁)
if (gender == "男" and age == 5) or (gender == "女" and age > 7):
    print("能推动桌子")
else:
    print("不能推动桌子")

另一种写法(嵌套if)

gender = input("请输入性别(男/女):")
age = int(input("请输入年龄:"))

if gender == "男":
    if age == 5:
        print("能推动桌子")
    else:
        print("不能推动桌子")
else:  # 性别是"女"
    if age > 7:
        print("能推动桌子")
    else:
        print("不能推动桌子")

if-elif-else语句:多种选择

当需要在三种或更多情况中做选择时,我们使用if-elif-else语句。

if-elif-else的基本语法

if 条件1:
    # 条件1为True时执行
    代码块1
elif 条件2:
    # 条件2为True时执行
    代码块2
elif 条件3:
    # 条件3为True时执行
    代码块3
...
else:
    # 所有条件都不满足时执行
    代码块n

语法要点

  • elifelse if的缩写
  • 可以有多个elif
  • else是可选的
  • 程序会从上到下检查条件,一旦某个条件满足,就执行对应的代码块,然后跳出整个if语句

示例1:成绩分级

从键盘输入小明的Python期末成绩:

  • 当成绩为100分时,奖励一张出游机票
  • 当成绩为[80-99]时,奖励一个iPhone
  • 当成绩为[60-79]时,奖励一本参考书
  • 其他时(低于60),什么奖励也没有
# 输入分数
score = int(input("请输入Python期末成绩:"))

# 判断分数等级
if score == 100:
    print("🎉 完美!奖励一张出游机票!")
elif score >= 80:  # 80-99分
    print("👏 很棒!奖励一个iPhone!")
elif score >= 60:  # 60-79分
    print("👍 及格了!奖励一本参考书!")
else:  # 低于60分
    print("😢 继续努力,没有奖励...")

print("判断结束")

运行示例1(100分):

请输入Python期末成绩:100
🎉 完美!奖励一张出游机票!
判断结束

运行示例2(85分):

请输入Python期末成绩:85
👏 很棒!奖励一个iPhone!
判断结束

运行示例3(65分):

请输入Python期末成绩:65
👍 及格了!奖励一本参考书!
判断结束

运行示例4(50分):

请输入Python期末成绩:50
😢 继续努力,没有奖励...
判断结束

👨‍🏫 给家长的Tips

教学方法建议:

  • 解释为什么score >= 80不需要写成score >= 80 and score < 100
  • 因为如果score是100,第一个条件就满足了,不会继续检查
  • 这种写法叫“短路“逻辑,效率更高
  • 可以在纸上画数轴,标出不同分数段的位置

数学知识融入:分段函数 📐

在数学中,我们学过分段函数,就是一个函数在不同区间有不同的表达式。

例如,快递费的计算:

  • 重量 ≤ 1kg:5元
  • 1kg < 重量 ≤ 3kg:10元
  • 重量 > 3kg:15元

这就是一个分段函数!用if-elif-else可以轻松实现!

数学练习:快递费计算 📝

# 输入快递重量(kg)
weight = float(input("请输入快递重量(kg): "))

# 根据重量计算费用(分段函数)
if weight <= 1:
    fee = 5
elif weight <= 3:  # 注意:1 < weight ≤ 3
    fee = 10
else:  # weight > 3
    fee = 15

print(f"快递费用为: {fee}元")

数学小挑战 🌟

出租车计费也是分段函数:

  • 3公里以内:起步价10元
  • 3-10公里:每公里2元
  • 10公里以上:每公里3元

你能写出计算出租车费用的代码吗?

👉 点击查看答案
# 输入里程数(km)
distance = float(input("请输入里程数(km): "))

# 根据里程计算费用
if distance <= 3:
    fare = 10
elif distance <= 10:  # 3 < distance ≤ 10
    fare = 10 + (distance - 3) * 2
else:  # distance > 10
    fare = 10 + (10 - 3) * 2 + (distance - 10) * 3

print(f"出租车费用为: {fare}元")

数学应用题练习 📝

题目1:年龄分类 写一个程序,根据年龄输出人生阶段:

  • 0-6岁:婴幼儿
  • 7-12岁:小学生
  • 13-15岁:初中生
  • 16-18岁:高中生
  • 19-22岁:大学生
  • 23-60岁:成年人
  • 60岁以上:老年人
👉 点击查看答案
# 输入年龄
age = int(input("请输入年龄: "))

# 判断人生阶段
if age <= 6:
    print("婴幼儿 👶")
elif age <= 12:  # 7-12岁
    print("小学生 🎒")
elif age <= 15:  # 13-15岁
    print("初中生 📚")
elif age <= 18:  # 16-18岁
    print("高中生 🏫")
elif age <= 22:  # 19-22岁
    print("大学生 🎓")
elif age <= 60:  # 23-60岁
    print("成年人 💼")
else:  # 60岁以上
    print("老年人 👴")

示例2:周末活动计划

周末组织活动:

  • 如果是晴天,就出去郊游
  • 如果是阴天,就去参观动物园
  • 如果是下雨天,就去室内游乐场
# 输入天气
weather = input("今天是什么天气?(晴天/阴天/雨天):")

# 根据天气决定活动
if weather == "晴天":
    print("我们去郊游吧!")
elif weather == "阴天":
    print("我们去参观动物园吧!")
elif weather == "雨天":
    print("我们去室内游乐场玩吧!")
else:
    print("输入错误,请重新输入!")

print("活动计划结束")

示例3:学生成绩等级

根据成绩判断等级:

  • 100分:best
  • 90-99分:very good
  • 80-89分:good
  • 60-79分:pass
  • 低于60分:not pass
# 输入分数
score = int(input("请输入学生成绩:"))

# 判断等级
if score == 100:
    print("等级:best 🌟")
elif score >= 90:
    print("等级:very good 👍")
elif score >= 80:
    print("等级:good 🙂")
elif score >= 60:
    print("等级:pass 😐")
else:
    print("等级:not pass 😞")

print("判断结束")

练习3

题目:根据用户输入的数字判断是奇数还是偶数。

👉 点击查看答案
# 输入数字
num = int(input("请输入一个整数:"))

# 判断奇偶
if num % 2 == 0:
    print(f"{num}是偶数")
else:
    print(f"{num}是奇数")

练习4

题目:根据指定的月份打印该月份所属的季节。(3-5月:春天,6-8月:夏天,9-11月:秋天,12-2月:冬天)

👉 点击查看答案
# 输入月份
month = int(input("请输入月份(1-12):"))

# 判断季节
if 3 <= month <= 5:
    print("春天 🌸")
elif 6 <= month <= 8:
    print("夏天 ☀️")
elif 9 <= month <= 11:
    print("秋天 🍂")
else:
    print("冬天 ❄️")

也可以用or写法

month = int(input("请输入月份(1-12):"))

if month == 3 or month == 4 or month == 5:
    print("春天 🌸")
elif month == 6 or month == 7 or month == 8:
    print("夏天 ☀️")
elif month == 9 or month == 10 or month == 11:
    print("秋天 🍂")
else:
    print("冬天 ❄️")

嵌套if语句:条件中的条件

有时候,我们需要在一个条件判断内部再进行条件判断,这就是嵌套if语句

嵌套if的基本语法

if 外层条件:
    if 内层条件:
        # 外层和内层条件都满足时执行
        代码块1
    else:
        # 外层满足但内层不满足时执行
        代码块2
else:
    # 外层条件不满足时执行
    代码块3

注意:每层if都要有自己的缩进!

示例1:活动计划(考虑工作日和天气)

活动计划的安排:

  • 如果今天是工作日,则去上班
  • 如果今天是周末,则外出游玩
    • 同时,如果周末天气晴朗,则去室外游乐场游玩
    • 否则去室内游乐场游玩
# 输入星期几和天气
day = input("今天是星期几?(1-7,1是周一):")
weather = input("今天天气如何?(晴天/其他):")

# 判断是工作日还是周末
if day in ["1", "2", "3", "4", "5"]:
    print("今天是工作日,要去上班 😢")
else:  # 周末(6或7)
    print("今天是周末!🎉")
    # 嵌套判断天气
    if weather == "晴天":
        print("天气晴朗,去室外游乐场玩!")
    else:
        print("天气不好,去室内游乐场玩!")

示例2:运动会决赛分组

学校举行运动会,百米赛跑跑入10秒内的学生有资格进决赛,根据性别分别进入男子组和女子组。

# 输入成绩和性别
time = float(input("请输入百米赛跑成绩(秒):"))
gender = input("请输入性别(男/女):")

# 判断是否有资格进决赛
if time < 10:
    print("恭喜!你有资格进入决赛!")
    # 嵌套判断性别分组
    if gender == "男":
        print("你被分到男子组 🏃‍♂️")
    else:
        print("你被分到女子组 🏃‍♀️")
else:
    print("很遗憾,成绩未达标,不能进决赛")

给家长的小贴士

  • 解释“嵌套“的概念:一个if在另一个if里面
  • 强调缩进的重要性:每层都有自己的缩进
  • 建议先用自然语言描述逻辑,再转化为代码

练习5

题目:写一个简单的猜数字游戏。程序随机设定一个数字(可以先固定为7),用户输入猜测的数字,程序给出提示“太大了“、“太小了“或“猜对了”。

👉 点击查看答案
# 设定目标数字
target = 7

# 用户输入
guess = int(input("请输入你猜的数字(1-10):"))

# 判断大小
if guess == target:
    print("🎉 恭喜你,猜对了!")
else:
    if guess > target:
        print("太大了!再试一次")
    else:
        print("太小了!再试一次")

print("游戏结束")

运行示例

请输入你猜的数字(1-10):5
太小了!再试一次
游戏结束

请输入你猜的数字(1-10):9
太大了!再试一次
游戏结束

请输入你猜的数字(1-10):7
🎉 恭喜你,猜对了!
游戏结束

综合应用案例

案例1:计算周数和剩余天数

用户输入一个天数,可以输出包含多少周,以及剩余几天。

例如:

  • 输入57天,打印“8 weeks and 1 day“
  • 输入9天,打印“1 week and 2 days“
# 输入天数
days = int(input("请输入总天数:"))

# 计算周数和剩余天数
weeks = days // 7
remaining_days = days % 7

# 根据数量决定单复数
if weeks == 1:
    week_str = "week"
else:
    week_str = "weeks"

if remaining_days == 1:
    day_str = "day"
else:
    day_str = "days"

# 输出结果
print(f"{weeks} {week_str} and {remaining_days} {day_str}")

运行示例

请输入总天数:57
8 weeks and 1 day
请输入总天数:9
1 week and 2 days

给家长的小贴士

  • 解释//(整除)和%(求余)的区别
  • 强调英语中单数和复数的区别
  • 这是一个综合练习,涉及运算、条件判断、字符串格式化

案例2:计算四位数数字之和

从键盘输入任意一个四位数,计算它四位数字相加的和。

# 输入一个四位数
num_str = input("请输入一个四位数:")

# 检查是否为四位数
if len(num_str) != 4:
    print("输入错误!请输入一个四位数!")
else:
    # 将字符串转换为数字
    num = int(num_str)

    # 分解每一位数字
    # 方法1:用字符串切片
    digit1 = int(num_str[0])
    digit2 = int(num_str[1])
    digit3 = int(num_str[2])
    digit4 = int(num_str[3])

    # 计算和
    total = digit1 + digit2 + digit3 + digit4

    print(f"四位数字之和为:{total}")

运行示例

请输入一个四位数:1234
四位数字之和为:10

案例3:判断闰年

写出可以判定某一年是否是闰年的逻辑表达式。

闰年规则

  • 能被4整除但不能被100整除,或者能被400整除的年份
# 输入年份
year = int(input("请输入年份:"))

# 判断闰年
if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
    print(f"{year}年是闰年 📅")
else:
    print(f"{year}年不是闰年")

运行示例

请输入年份:2024
2024年是闰年 📅
请输入年份:1900
1900年不是闰年
请输入年份:2000
2000年是闰年 📅

给家长的小贴士

  • 解释闰年的历史背景(为什么会有闰年)
  • 强调逻辑运算符的优先级:and优先于or
  • 可以让孩子计算自己出生年份是否是闰年

案例4:狗狗年龄换算

根据用户输入的狗狗的年龄得出相当于人的多少岁。

换算规则

  • 狗狗的前2年,第一年相当于人的10.5岁,第二年也相当于10.5岁
  • 往后每一年相当于4岁计算
  • 狗狗最长活20年
# 输入狗狗年龄
dog_age = float(input("请输入狗狗的年龄:"))

# 检查年龄是否合理
if dog_age < 0 or dog_age > 20:
    print("狗狗的年龄应该在0-20岁之间!")
else:
    # 计算相当于人的年龄
    if dog_age <= 2:
        # 前两年,每年10.5岁
        human_age = dog_age * 10.5
    else:
        # 前两年21岁,往后每年4岁
        human_age = 21 + (dog_age - 2) * 4

    print(f"狗狗{dog_age}岁,相当于人类{human_age}岁")

运行示例

请输入狗狗的年龄:1
狗狗1.0岁,相当于人类10.5岁
请输入狗狗的年龄:5
狗狗5.0岁,相当于人类37.0岁

案例5:找三个数中的最大值

用户输入三个数,输出最大的。

# 输入三个数
num1 = float(input("请输入第一个数:"))
num2 = float(input("请输入第二个数:"))
num3 = float(input("请输入第三个数:"))

# 找最大值
if num1 >= num2 and num1 >= num3:
    max_num = num1
elif num2 >= num1 and num2 >= num3:
    max_num = num2
else:
    max_num = num3

print(f"最大的数是:{max_num}")

运行示例

请输入第一个数:5
请输入第二个数:12
请输入第三个数:8
最大的数是:12.0

另一种写法(逐步比较)

num1 = float(input("请输入第一个数:"))
num2 = float(input("请输入第二个数:"))
num3 = float(input("请输入第三个数:"))

# 假设第一个数最大
max_num = num1

# 如果第二个数更大,更新最大值
if num2 > max_num:
    max_num = num2

# 如果第三个数更大,更新最大值
if num3 > max_num:
    max_num = num3

print(f"最大的数是:{max_num}")

给家长的小贴士

  • 两种方法各有优缺点,可以讨论哪种更好理解
  • 第二种方法更容易扩展到更多数字
  • 为第10章(List)铺垫:可以用list和max()函数简化

常见错误和调试

错误1:忘记冒号

# 错误:if后面没有冒号
if score > 60
    print("及格了")

# 正确
if score > 60:
    print("及格了")

错误2:缩进不正确

# 错误:缩进不一致
if score > 60:
  print("及格了")  # 2个空格
    print("恭喜!")  # 4个空格

# 正确:缩进一致
if score > 60:
    print("及格了")
    print("恭喜!")

错误3:赋值和比较混淆

# 错误:用=而不是==
if score = 100:
    print("完美")

# 正确:用==比较
if score == 100:
    print("完美")

错误4:条件顺序错误

score = 85

# 错误:顺序错了,永远无法判断到80-89
if score >= 60:
    print("及格")
elif score >= 80:
    print("良好")
elif score >= 90:
    print("优秀")

# 正确:从高到低判断
if score >= 90:
    print("优秀")
elif score >= 80:
    print("良好")
elif score >= 60:
    print("及格")

给家长的小贴士

  • 教孩子学会看错误信息(SyntaxError, IndentationError等)
  • 使用print语句在关键位置输出中间结果,帮助调试
  • 用简单的例子测试条件是否正确

条件表达式(三元运算符)

Python还提供了一种简洁的条件表达式,也叫三元运算符

基本语法

结果1 if 条件 else 结果2

解释:如果条件为True,返回结果1;否则返回结果2。

示例:找出两个数中的较大值

# 传统写法
a = 10
b = 20

if a > b:
    max_num = a
else:
    max_num = b

print(f"较大的数是:{max_num}")

用条件表达式简化

a = 10
b = 20

max_num = a if a > b else b

print(f"较大的数是:{max_num}")

练习6

题目:用条件表达式判断一个数是正数、负数还是零,并输出“正数“、“负数“或“零”。

👉 点击查看答案
num = float(input("请输入一个数字:"))

result = "正数" if num > 0 else "负数" if num < 0 else "零"

print(f"{num}是{result}")

注意:这种嵌套的三元表达式可读性较差,不建议过度使用。

综合练习:星期几判断器

题目:请输入星期几的第一个字母来判断一下是星期几,如果第一个字母一样,则继续判断第二个字母。

# 输入第一个字母
first_letter = input("请输入星期几的第一个字母:").lower()

# 判断第一个字母
if first_letter == 'm':
    print("星期一 Monday")
elif first_letter == 't':
    # 需要判断第二个字母
    second_letter = input("请输入第二个字母:").lower()
    if second_letter == 'u':
        print("星期二 Tuesday")
    elif second_letter == 'h':
        print("星期四 Thursday")
    else:
        print("输入错误!")
elif first_letter == 'w':
    print("星期三 Wednesday")
elif first_letter == 'f':
    print("星期五 Friday")
elif first_letter == 's':
    # 需要判断第二个字母
    second_letter = input("请输入第二个字母:").lower()
    if second_letter == 'a':
        print("星期六 Saturday")
    elif second_letter == 'u':
        print("星期日 Sunday")
    else:
        print("输入错误!")
else:
    print("输入错误!")

运行示例

请输入星期几的第一个字母:s
请输入第二个字母:a
星期六 Saturday
请输入星期几的第一个字母:t
请输入第二个字母:u
星期二 Tuesday

章节小结

我们学到了什么

  1. 条件语句:让程序能够根据不同情况做不同决定
  2. if语句:最简单的条件判断
  3. if-else语句:两种选择
  4. if-elif-else语句:多种选择
  5. 嵌套if语句:条件中的条件
  6. 条件表达式:简洁的三元运算符

重要语法回顾

语句结构用途
if 条件:单一条件判断
if-else:两种选择
if-elif-else:多种选择
嵌套if复杂条件判断

编程技巧

  1. 缩进很重要:Python用缩进来组织代码块
  2. 条件顺序:在if-elif中,条件从上到下检查,满足即停止
  3. 冒号不能忘:if、elif、else后面都要有冒号
  4. 布尔运算:用and、or、not组合多个条件
  5. 调试方法:用print输出中间结果,帮助定位问题

下一步

在下一章(第8章),我们将学习循环语句(for和while),让程序能够重复执行某些操作。结合条件语句和循环,我们就能写出更强大的程序!

挑战练习

  1. 必做

    • 写一个程序,输入三个数,输出最小的数
    • 写一个程序,输入年份和月份,输出该月有多少天
    • 写一个程序,输入三角形的三边长,判断能否构成三角形
  2. 选做

    • 写一个简单的计算器,可以加减乘除
    • 写一个石头剪刀布游戏
    • 写一个温度转换器(摄氏度/华氏度/开尔文)
  3. 挑战

    • 写一个猜数字游戏,限制猜测次数(比如5次)
    • 写一个登录系统,可以有注册、登录、修改密码功能
    • 用Turtle库画一个图形,根据用户输入的数字决定画什么图形

加油!你已经学会让程序做决定的能力了!🎉

循环语句

引言

在前面的章节中,我们学习了顺序语句和条件语句。顺序语句让程序按顺序执行,条件语句让程序根据不同情况做不同决定。

但是,如果需要重复做某些事情呢?

想象一下:

  • 老师让你把“我要好好学习“这句话写100遍
  • 你要帮妈妈数一共有多少颗糖果
  • 计算从1加到100的和
  • 画一个正方形,要画4条边

这些都需要重复执行相同的操作。在Python中,我们用循环语句来实现这种功能。

这一章,我们将学习:

  • 什么是循环
  • while循环(条件循环)
  • for循环(计数循环)
  • 循环嵌套(循环中的循环)
  • break和continue(控制循环)
  • 用循环解决实际问题

为什么需要循环

让我们先看一个例子:假设我们要打印5次“Python真有趣!“。

不用循环的方法

print("Python真有趣!")
print("Python真有趣!")
print("Python真有趣!")
print("Python真有趣!")
print("Python真有趣!")

问题:

  • 需要写很多重复的代码
  • 如果要打印100次,会非常麻烦
  • 代码容易出错,难以维护

用循环的方法

# 用循环打印5次
count = 0
while count < 5:
    print("Python真有趣!")
    count = count + 1

优势:

  • 代码简洁,只写一次
  • 想打印多少次都可以,只需修改条件
  • 容易维护和修改

给家长的小贴士:

  • 用生活中的例子解释循环:绕操场跑圈、抄写生字、数数
  • 强调循环的好处:省时、省力、不容易出错
  • 可以让孩子想想生活中有哪些“重复“的事情

💡 计算机为什么擅长重复工作?

CPU的“超能力“——重复执行

在第6章,我们学习了CPU是如何执行指令的:

  • 取指令译码执行重复

现在,我们要学习循环,这正好发挥了CPU最强大的能力——高速重复执行!

比喻时间 🎯 想象你和计算机比赛抄写生字:

  • :抄写100遍“我要好好学习“,需要1-2小时,手会酸
  • CPU:每秒能执行几十亿次运算,100遍对它来说不到0.000001秒!

数学小挑战 🧮 让我们算一算CPU有多快:

示例:假设有一个1GHz的CPU(每秒执行10亿次运算)

  • 每秒能执行:1,000,000,000次运算
  • 执行100次简单循环需要:100 ÷ 1,000,000,000 = 0.0000001秒!
  • 执行1,000,000次(100万次)需要:0.001秒!
  • 执行1,000,000,000次(10亿次)需要:1秒
# 让我们感受一下CPU的速度
import time

# 测试:执行1000次循环
start = time.time()  # 记录开始时间

for i in range(1000):
    result = i * 2  # 简单的乘法

end = time.time()  # 记录结束时间

print(f"执行1000次循环用时:{(end - start) * 1000:.4f}毫秒")
print(f"如果每秒能执行10亿次运算,1000次只需要0.000001秒!")

运行结果(因电脑而异):

执行1000次循环用时:0.0980毫秒
如果每秒能执行10亿次运算,1000次只需要0.000001秒!

👨‍🏫 给家长的Tips

知识点补充(针对家长):

  • CPU主频(如2GHz、3GHz)表示每秒的时钟周期数
  • 1GHz = 10亿个周期/秒,3GHz = 30亿个周期/秒
  • 每个周期可能执行1-4条指令(现代CPU有指令级并行)
  • 所以一个3GHz CPU理论上每秒可执行几十亿到上百亿条指令!

如何给孩子讲解:

  • 不需要深入技术细节
  • 重点让孩子理解:计算机不怕重复,速度极快
  • 用“抄写生字“的类比:人讨厌重复,但计算机擅长重复
  • 这就是为什么我们用循环——让计算机发挥它的“超能力“!

为什么计算机适合做重复性工作?

人类 vs 计算机:

任务人类计算机
抄写100遍生字1-2小时,手酸,无聊不到0.001秒,不会累
数1到100万几天,数错概率高不到1秒,100%准确
搜索1000个名字几十分钟,会疲劳不到0.1秒,不会错
画1000个正方形几小时,画得不一样几秒钟,一模一样

人类的优势:

  • ✅ 创造力(画画、写作、发明)
  • ✅ 情感理解(理解笑话、安慰人)
  • ✅ 灵活适应(处理突发情况)

计算机的优势:

  • ✅ 高速重复(不嫌累、不嫌烦)
  • ✅ 精确无误(不会算错)
  • ✅ 海量存储(记住无数数据)

结论 🎯

  • 让人类做创造性的工作(设计游戏、写故事、发明)
  • 让计算机做重复性的工作(计算、搜索、排序)
  • 循环就是让计算机发挥重复工作的“超能力“!

👨‍🏫 给家长的Tips

教育意义:

  • 让孩子理解:学习编程不是为了取代人类
  • 而是为了让计算机帮我们做重复、枯燥的工作
  • 这样人类就能把精力花在更有创造性的任务上
  • 这就是“人机协作“的思想

生活中的例子:

  • 计算器:做复杂的数学计算(重复性工作)
  • 搜索引擎:在亿万网页中搜索信息(重复性工作)
  • Excel:自动计算1000行数据的总和(重复性工作)
  • 人类:设计算法、写文章、创作艺术(创造性工作)

while循环:条件循环

while循环是条件循环,只要条件为True,就一直重复执行代码块。

while循环的基本语法

while 条件:
    # 条件为True时重复执行的代码
    代码块

语法要点:

  • while后面跟着一个条件(布尔表达式)
  • 条件后面必须有冒号:
  • 要执行的代码必须缩进
  • 当条件为False时,循环结束

循环的三个重要元素

  1. 初始值: 循环变量的初始值
  2. 条件: 判断是否继续循环
  3. 更新: 循环变量如何变化

示例模式:

# 1. 初始值
count = 0

# 2. 条件
while count < 5:
    print(f"第{count + 1}次: Python真有趣!")

    # 3. 更新循环变量
    count = count + 1

print("循环结束!")

输出结果:

第1次: Python真有趣!
第2次: Python真有趣!
第3次: Python真有趣!
第4次: Python真有趣!
第5次: Python真有趣!
循环结束!

给家长的小贴士:

  • 重点讲解“循环变量更新“的重要性
  • 如果忘记更新,会变成“死循环“(无限循环)
  • 可以用“计数器“的概念来解释循环变量
  • 建议画出表格,记录每次循环时变量的值

示例1:打印数字

# 打印1到5的数字
count = 1
while count <= 5:
    print(count)
    count = count + 1

print("打印完成!")

输出结果:

1
2
3
4
5
打印完成!

示例2:累加求和

# 计算1到5的和
total = 0      # 累积变量,保存总和
count = 1      # 计数变量

while count <= 5:
    total = total + count  # 累加
    print(f"加{count}, 当前和:{total}")
    count = count + 1

print(f"1到5的和是:{total}")

输出结果:

加1, 当前和:1
加2, 当前和:3
加3, 当前和:6
加4, 当前和:10
加5, 当前和:15
1到5的和是:15

给家长的小贴士:

  • 用“存钱罐“类比累积变量
  • 每次循环往存钱罐里加钱
  • 循环结束时,存钱罐里的钱就是总和
  • 强调初始值的重要性:total要从0开始

示例3:打印三角形*

# 打印一个直角三角形
line = 1
while line <= 5:
    # 每行打印line个*
    print("*" * line)
    line = line + 1

输出结果:

*
**
***
****
*****

练习1:打印一棵圣诞树

题目:用while循环打印一棵圣诞树。

要求:

  • 树冠:3层,每层*数量递增
  • 树干:1层,用|表示
👉 点击查看答案
# 打印圣诞树
# 树冠部分
line = 1
while line <= 3:
    # 打印空格(居中)
    spaces = " " * (3 - line)
    # 打印*
    stars = "*" * (2 * line - 1)
    print(spaces + stars)
    line = line + 1

# 树干部分
print("  |  ")  # 树干
print("  |  ")  # 树干

输出结果:

  *
 ***
*****
  |
  |

简单版本:

# 简单的圣诞树
line = 1
while line <= 3:
    print("*" * line)
    line = line + 1
print("*")  # 树干

while循环中嵌套条件

在循环中,我们可以结合条件语句,实现更复杂的逻辑。

示例1:打印偶数

# 打印1到10中所有的偶数
count = 1
while count <= 10:
    # 判断是否为偶数
    if count % 2 == 0:
        print(f"{count}是偶数")
    count = count + 1

print("打印完成!")

输出结果:

2是偶数
4是偶数
6是偶数
8是偶数
10是偶数
打印完成!

示例2:标记特殊数字

# 打印1到10,在3的倍数前加标记
count = 1
while count <= 10:
    # 判断是否为3的倍数
    if count % 3 == 0:
        print(f"[3的倍数] {count}")
    else:
        print(count)
    count = count + 1

输出结果:

1
2
[3的倍数] 3
4
5
[3的倍数] 6
7
8
[3的倍数] 9
10

示例3:统计奇数和偶数的和

# 计算1到10之间偶数和奇数的和
even_sum = 0   # 偶数和
odd_sum = 0    # 奇数和
count = 1

while count <= 10:
    if count % 2 == 0:
        # 偶数加到even_sum
        even_sum = even_sum + count
        print(f"{count}是偶数, 偶数和:{even_sum}")
    else:
        # 奇数加到odd_sum
        odd_sum = odd_sum + count
        print(f"{count}是奇数, 奇数和:{odd_sum}")
    count = count + 1

print(f"\n偶数和:{even_sum}")
print(f"奇数和:{odd_sum}")

输出结果:

1是奇数, 奇数和:1
2是偶数, 偶数和:2
3是奇数, 奇数和:4
4是偶数, 偶数和:6
5是奇数, 奇数和:9
6是偶数, 偶数和:12
7是奇数, 奇数和:16
8是偶数, 偶数和:20
9是奇数, 奇数和:25
10是偶数, 偶数和:30

偶数和:30
奇数和:25

给家长的小贴士:

  • 解释“累加器“的概念
  • 每个累加器都要有自己的初始值
  • 建议画图理解:两个篮子,分别装奇数和偶数
  • 强调print语句在循环中的调试作用

练习2:倒三角形

题目:用while循环打印倒三角形。

👉 点击查看答案
# 打印倒三角形
line = 5
while line >= 1:
    print("*" * line)
    line = line - 1

输出结果:

*****
****
***
**
*

练习3:求能被3整除的数字的乘积

题目:计算1到10之间能被3整除的数字的乘积。

👉 点击查看答案
# 计算1到10中能被3整除的数字的乘积
product = 1  # 乘积的初始值是1,不是0!
count = 1

while count <= 10:
    if count % 3 == 0:
        product = product * count
        print(f"乘{count}, 当前乘积:{product}")
    count = count + 1

print(f"乘积是:{product}")

输出结果:

乘3, 当前乘积:3
乘6, 当前乘积:18
乘9, 当前乘积:162
乘积是:162

重要提示:

  • 乘积的初始值必须是1,不能是0!
  • 如果初始值是0,乘积永远是0

for循环和range函数

for循环通常和range()函数一起使用,用于已知循环次数的情况。

range函数

range()函数生成一个数字序列。

基本用法:

# range(开始, 结束, 步长)
# 注意:结束值不包含在内!

示例:

# range(5): 生成0, 1, 2, 3, 4
for i in range(5):
    print(i)

print("------")

# range(1, 6): 生成1, 2, 3, 4, 5
for i in range(1, 6):
    print(i)

print("------")

# range(1, 10, 2): 生成1, 3, 5, 7, 9 (奇数)
for i in range(1, 10, 2):
    print(i)

print("------")

# range(10, 0, -1): 生成10, 9, 8, 7, 6, 5, 4, 3, 2, 1 (倒数)
for i in range(10, 0, -1):
    print(i)

给家长的小贴士:

  • 重点强调“结束值不包含“
  • range(1, 5)生成1, 2, 3, 4,不包括5
  • 用“排队“类比:从1号排到4号,5号不排
  • 步长可以是负数,实现倒序

for循环的基本语法

for 变量 in range(开始, 结束):
    # 循环体

示例1:打印数字

# 用for循环打印1到5
for i in range(1, 6):
    print(i)

print("完成!")

输出结果:

1
2
3
4
5
完成!

示例2:求和

# 用for循环计算1到5的和
total = 0
for i in range(1, 6):
    total = total + i
    print(f"加{i}, 当前和:{total}")

print(f"1到5的和是:{total}")

示例3:遍历字符串

# 遍历字符串中的每个字符
name = "Python"
for ch in name:
    print(ch)

输出结果:

P
y
t
h
o
n

while vs for:如何选择?

特点while循环for循环
使用场景不知道循环次数知道循环次数
循环条件根据条件判断遍历序列
需要手动更新循环变量自动更新变量
例子“直到用户输入正确”“打印1到100”

示例对比:

# while循环版本
count = 1
while count <= 5:
    print(count)
    count = count + 1

print("------")

# for循环版本(更简洁!)
for i in range(1, 6):
    print(i)

给家长的小贴士:

  • 如果知道循环次数,优先用for循环
  • for循环更简洁,不容易出错(自动更新变量)
  • while循环更灵活,适合复杂条件
  • 建议让孩子用两种方法写同一个程序,体会区别

break和continue

在循环中,我们可以用breakcontinue来控制循环的执行。

break:跳出循环

break立即终止整个循环。

示例:

# 找到第一个能被7整除的数
for i in range(1, 100):
    if i % 7 == 0:
        print(f"找到了!第一个能被7整除的数是:{i}")
        break  # 找到就退出循环

print("程序结束")

输出结果:

找到了!第一个能被7整除的数是:7
程序结束

continue:跳过本次循环

continue跳过本次循环,直接进入下一次循环。

示例:

# 打印1到10中的奇数(跳过偶数)
for i in range(1, 11):
    if i % 2 == 0:
        continue  # 跳过偶数,不执行后面的print
    print(i)

print("打印完成")

输出结果:

1
3
5
7
9
打印完成

示例:累加直到和大于等于100

# 计算1+2+3+...,直到和大于等于100
total = 0
count = 1

while True:  # 无限循环
    total = total + count
    print(f"加{count}, 当前和:{total}")

    if total >= 100:
        print(f"和达到{total}, 停止!")
        break  # 达到目标,退出循环

    count = count + 1

输出结果:

加1, 当前和:1
加2, 当前和:3
加3, 当前和:6
...
加13, 当前和:91
加14, 当前和:105
和达到105, 停止!

示例:QQ登录验证

# 模拟QQ登录
correct_username = "admin"
correct_password = "123456"

while True:
    # 输入用户名和密码
    username = input("请输入用户名:")
    password = input("请输入密码:")

    # 验证
    if username == correct_username and password == correct_password:
        print("登录成功!")
        break  # 登录成功,退出循环
    else:
        print("用户名或密码错误,请重新输入!\n")

print("欢迎进入系统!")

给家长的小贴士:

  • break就像“紧急出口“,随时可以离开循环
  • continue就像“跳过“,这次不做,继续下一次
  • while True是一个无限循环,必须有break才能退出
  • 强调滥用break和continue会让代码难以理解
  • 建议先用普通循环写法,熟练后再用这些控制语句

循环嵌套

一个循环内部可以再包含另一个循环,这就是循环嵌套

示例1:打印数字矩形

# 打印数字矩形
for i in range(1, 4):  # 外层循环:控制行
    for j in range(1, 4):  # 内层循环:控制列
        print(f"{i}", end=" ")  # end=" "表示不换行
    print()  # 每行结束后换行

输出结果:

1 1 1
2 2 2
3 3 3

示例2:打印乘法口诀表

# 打印乘法口诀表(9x9)
for i in range(1, 10):  # 行:1到9
    for j in range(1, i + 1):  # 列:1到i
        result = i * j
        print(f"{j}x{i}={result}", end="\t")  # \t是制表符
    print()  # 换行

输出结果:

1x1=1
1x2=2	2x2=4
1x3=3	2x3=6	3x3=9
1x4=4	2x4=8	3x4=12	4x4=16
1x5=5	2x5=10	3x5=15	4x5=20	5x5=25
1x6=6	2x6=12	3x6=18	4x6=24	5x6=30	6x6=36
1x7=7	2x7=14	3x7=21	4x7=28	5x7=35	6x7=42	7x7=49
1x8=8	2x8=16	3x8=24	4x8=32	5x8=40	6x8=48	7x8=56	8x8=64
1x9=9	2x9=18	3x9=27	4x9=36	5x9=45	6x9=54	7x9=63	8x9=72	9x9=81

给家长的小贴士:

  • 用“时钟“类比嵌套循环:时针和分针
  • 外层循环执行一次,内层循环执行完整一轮
  • 建议画出执行过程,理解嵌套逻辑
  • 强调缩进的重要性:内层循环要多缩进一层

示例3:找出所有三位数

题目:用1、2、3、4四个数字,能组成多少个三位数?

# 用1、2、3、4组成三位数
count = 0  # 计数器

print("可以组成的三位数:")

for i in [1, 2, 3, 4]:  # 百位
    for j in [1, 2, 3, 4]:  # 十位
        for k in [1, 2, 3, 4]:  # 个位
            num = i * 100 + j * 10 + k
            print(num, end=" ")
            count = count + 1

            # 每行打印10个
            if count % 10 == 0:
                print()

print(f"\n总共可以组成{count}个三位数")

示例4:找出无重复数字的三位数

题目:用1、2、3、4四个数字,能组成多少个无重复数字的三位数?

# 用1、2、3、4组成无重复数字的三位数
count = 0

print("无重复数字的三位数:")

for i in [1, 2, 3, 4]:  # 百位
    for j in [1, 2, 3, 4]:  # 十位
        if j == i:  # 十位和百位不能相同
            continue
        for k in [1, 2, 3, 4]:  # 个位
            if k == i or k == j:  # 个位不能和百位、十位相同
                continue
            num = i * 100 + j * 10 + k
            print(num, end=" ")
            count = count + 1

            # 每行打印10个
            if count % 10 == 0:
                print()

print(f"\n总共可以组成{count}个无重复数字的三位数")

输出结果:

无重复数字的三位数:
123 124 132 134 142 143 213 214 231 234
241 243 312 314 321 324 341 342 412 413
421 423 431 432
总共可以组成24个无重复数字的三位数

常见循环模式和技巧

模式1:计数器模式

# 计数器:统计满足条件的数量
count = 0
for i in range(1, 101):
    if i % 7 == 0:
        count = count + 1

print(f"1到100之间有{count}个数能被7整除")

模式2:累加器模式

# 累加器:求和
total = 0
for i in range(1, 101):
    total = total + i

print(f"1到100的和是:{total}")

模式3:累乘器模式

# 累乘器:求阶乘(5! = 5x4x3x2x1)
factorial = 1
for i in range(1, 6):
    factorial = factorial * i

print(f"5的阶乘是:{factorial}")

模式4:最大值/最小值查找

# 找最大值
numbers = [23, 45, 12, 67, 34, 89, 5]
max_num = numbers[0]  # 假设第一个是最大的

for num in numbers:
    if num > max_num:
        max_num = num

print(f"最大的数是:{max_num}")

模式5:正序和反序遍历

# 正序遍历
for i in range(1, 6):
    print(i, end=" ")
print()

# 反序遍历
for i in range(5, 0, -1):
    print(i, end=" ")
print()

# 步长为2
for i in range(1, 10, 2):
    print(i, end=" ")

给家长的小贴士:

  • 这些模式在编程中非常常见
  • 建议让孩子记住这些基本模式
  • 可以用生活中的例子类比:计数器、存钱罐、找最大数
  • 鼓励孩子用这些模式解决新问题

综合练习

练习1:打印30以内的自然数

题目:分别用while和for循环打印30以内的自然数(包含30)。

👉 点击查看答案

while版本:

# while循环
count = 0
while count <= 30:
    print(count, end=" ")
    count = count + 1

for版本:

# for循环
for i in range(31):  # range(31) = range(0, 31) = 0到30
    print(i, end=" ")

练习2:挑水问题

题目:一口缸容量有180升,一个和尚每次挑水20升,多少次挑满?

👉 点击查看答案
# 挑水问题
capacity = 180  # 缸的容量
each_time = 20  # 每次挑水量
current = 0     # 当前水量
count = 0       # 挑水次数

while current < capacity:
    current = current + each_time
    count = count + 1
    print(f"第{count}次挑水,当前水量:{current}升")

print(f"需要{count}次才能挑满")

输出结果:

第1次挑水,当前水量:20升
第2次挑水,当前水量:40升
...
第9次挑水,当前水量:180升
需要9次才能挑满

更简洁的写法:

capacity = 180
each_time = 20

# 用整除计算
times = capacity // each_time

# 如果有余数,需要多挑一次
if capacity % each_time != 0:
    times = times + 1

print(f"需要{times}次才能挑满")

练习3:统计学生成绩

题目:循环录入Java课的学生成绩,统计分数大于等于80分的学生比例。

👉 点击查看答案
# 统计学生成绩
total_students = int(input("请输入学生人数:"))
pass_count = 0  # 大于等于80分的人数

for i in range(total_students):
    score = int(input(f"请输入第{i + 1}个学生的成绩:"))

    if score >= 80:
        pass_count = pass_count + 1
        print(f"成绩{score}:优秀!")
    else:
        print(f"成绩{score}:继续努力!")

# 计算比例
percentage = (pass_count / total_students) * 100

print(f"\n统计结果:")
print(f"总人数:{total_students}")
print(f"优秀人数:{pass_count}")
print(f"优秀比例:{percentage:.1f}%")

运行示例:

请输入学生人数:5
请输入第1个学生的成绩:85
成绩85:优秀!
请输入第2个学生的成绩:72
成绩72:继续努力!
请输入第3个学生的成绩:90
成绩90:优秀!
请输入第4个学生的成绩:68
成绩68:继续努力!
请输入第5个学生的成绩:82
成绩82:优秀!

统计结果:
总人数:5
优秀人数:3
优秀比例:60.0%

练习4:小芳存钱

题目:小芳的妈妈每天给她2.5元钱,她都会存起来。从存钱开始,每过5天她就会花去6元钱。请问要到第几天,小芳才可以存满100元钱?

👉 点击查看答案
# 小芳存钱问题
money = 0  # 当前存款
day = 0    # 天数

while money < 100:
    day = day + 1
    money = money + 2.5  # 每天存2.5元
    print(f"第{day}天:存2.5元,当前存款:{money}元")

    # 每5天花6元
    if day % 5 == 0:
        money = money - 6
        print(f"  -> 第{day}天:花6元,剩余存款:{money}元")

print(f"\n第{day}天,小芳存满了100元!")

输出结果:

第1天:存2.5元,当前存款:2.5元
第2天:存2.5元,当前存款:5.0元
第3天:存2.5元,当前存款:7.5元
第4天:存2.5元,当前存款:10.0元
第5天:存2.5元,当前存款:12.5元
  -> 第5天:花6元,剩余存款:6.5元
...
第50天:存2.5元,当前存款:97.5元
第51天:存2.5元,当前存款:100.0元

第51天,小芳存满了100元!

练习5:求100以内偶数之和

题目:求100以内的偶数之和。

👉 点击查看答案

方法1:用if判断:

total = 0
for i in range(1, 101):
    if i % 2 == 0:
        total = total + i

print(f"100以内偶数之和:{total}")

方法2:用步长2(更高效):

total = 0
for i in range(0, 101, 2):  # 直接生成偶数
    total = total + i

print(f"100以内偶数之和:{total}")

输出结果:

100以内偶数之和:2550

练习6:求100以内6的倍数

题目:求出100以内的所有6的倍数,以及个数。

👉 点击查看答案
# 求100以内6的倍数
count = 0
print("100以内的6的倍数:")

for i in range(1, 101):
    if i % 6 == 0:
        print(i, end=" ")
        count = count + 1

print(f"\n总共{count}个")

输出结果:

100以内的6的倍数:
6 12 18 24 30 36 42 48 54 60 66 72 78 84 90 96
总共16个

练习7:打印数字矩形

题目:在控制台输出图形,第一行输出一个1,第二行输出二个2,第n行输出n个n。

👉 点击查看答案
# 打印数字矩形
n = int(input("请输入行数:"))

for i in range(1, n + 1):
    # 打印i个i
    for j in range(i):
        print(i, end=" ")
    print()  # 换行

运行示例:

请输入行数:5
1
2 2
3 3 3
4 4 4 4
5 5 5 5 5

练习8:水仙花数

题目:打印所有水仙花数。

什么是水仙花数?

  • 一个三位数
  • 例如153,1³ + 5³ + 3³ = 153
  • 即:各位数字的立方和等于它本身
👉 点击查看答案
# 打印水仙花数
print("三位数的水仙花数:")

for num in range(100, 1000):  # 遍历所有三位数
    # 分解百位、十位、个位
    hundreds = num // 100           # 百位
    tens = (num // 10) % 10        # 十位
    ones = num % 10                # 个位

    # 计算各位数字的立方和
    sum_of_cubes = hundreds ** 3 + tens ** 3 + ones ** 3

    # 判断是否为水仙花数
    if sum_of_cubes == num:
        print(num)

print("查找完成!")

输出结果:

三位数的水仙花数:
153
370
371
407
查找完成!

给家长的小贴士:

  • 解释“水仙花数“的含义
  • 重点讲解如何分解一个三位数的百位、十位、个位
  • 用除法和取余运算:
    • 百位 = num // 100
    • 十位 = (num // 10) % 10
    • 个位 = num % 10
  • 这是一个经典的编程练习,锻炼数学和编程结合的能力

练习9:交错数列求和

题目:计算1 - 2 + 3 - 4 + 5 - … + 99的结果。

👉 点击查看答案
# 计算交错数列求和: 1 - 2 + 3 - 4 + ... + 99
total = 0

for i in range(1, 100):  # 1到99
    if i % 2 == 1:
        # 奇数相加
        total = total + i
        print(f"+{i}", end=" ")
    else:
        # 偶数相减
        total = total - i
        print(f"-{i}", end=" ")

print(f"\n结果:{total}")

输出结果:

+1 -2 +3 -4 +5 -6 +7 -8 +9 -10 +11 -12 +13 -14 +15 -16 +17 -18 +19 -20 +21 -22 +23 -24 +25 -26 +27 -28 +29 -30 +31 -32 +33 -34 +35 -36 +37 -38 +39 -40 +41 -42 +43 -44 +45 -46 +47 -48 +49 -50 +51 -52 +53 -54 +55 -56 +57 -58 +59 -60 +61 -62 +63 -64 +65 -66 +67 -68 +69 -70 +71 -72 +73 -74 +75 -76 +77 -78 +79 -80 +81 -82 +83 -84 +85 -86 +87 -88 +89 -90 +91 -92 +93 -94 +95 -96 +97 -98 +99
结果:50

更简洁的写法:

# 方法2:观察规律
# (1-2) + (3-4) + (5-6) + ... + (97-98) + 99
# = -1 + -1 + -1 + ... + -1 + 99
# 共49个-1,最后+99
# = -49 + 99 = 50

# 或者换个角度:
# 1 + 3 + 5 + ... + 99 = 2500 (奇数和)
# 2 + 4 + 6 + ... + 98 = 2450 (偶数和)
# 2500 - 2450 = 50

odd_sum = 0
even_sum = 0

for i in range(1, 100):
    if i % 2 == 1:
        odd_sum = odd_sum + i
    else:
        even_sum = even_sum + i

result = odd_sum - even_sum
print(f"奇数和:{odd_sum}, 偶数和:{even_sum}, 结果:{result}")

练习10:猜数字游戏(限制次数)

题目:写一个猜数字游戏,猜对了结束,猜不对继续,最多猜9次。

👉 点击查看答案
# 猜数字游戏
import random  # 随机数库

target = random.randint(1, 100)  # 生成1到100的随机数
max_attempts = 9

print("=== 猜数字游戏 ===")
print(f"我已经想好了一个1到100之间的数字,你有{max_attempts}次机会猜!")

for attempt in range(1, max_attempts + 1):
    guess = int(input(f"\n第{attempt}次猜测,请输入你的猜测(1-100):"))

    if guess == target:
        print(f"🎉 恭喜你!第{attempt}次就猜对了!数字是:{target}")
        break  # 猜对了,退出循环
    elif guess < target:
        print(f"太小了!还有{max_attempts - attempt}次机会")
    else:
        print(f"太大了!还有{max_attempts - attempt}次机会")

else:
    # for循环正常结束(没有break)时执行
    print(f"\n😢 很遗憾,你没有猜对。正确答案是:{target}")

print("\n游戏结束!")

运行示例:

=== 猜数字游戏 ===
我已经想好了一个1到100之间的数字,你有9次机会猜!

第1次猜测,请输入你的猜测(1-100):50
太大了!还有8次机会

第2次猜测,请输入你的猜测(1-100):25
太小了!还有7次机会

第3次猜测,请输入你的猜测(1-100):37
太大了!还有6次机会

第4次猜测,请输入你的猜测(1-100):31
🎉 恭喜你!第4次就猜对了!数字是:31

游戏结束!

给家长的小贴士:

  • 这是一个综合练习,用到了循环、条件、break、else等
  • for...else语法:当for循环正常结束(没有break)时,执行else
  • 可以和孩子一起玩这个游戏,增加趣味性
  • 可以讨论如何用“二分查找“策略提高猜测效率

练习11:求素数

题目:输入一个正整数n,判断它是否为素数。

什么是素数?

  • 只能被1和它本身整除的数
  • 例如:2, 3, 5, 7, 11, 13, 17, 19, 23, …
  • 1不是素数
  • 2是最小的素数,也是唯一的偶数素数
👉 点击查看答案
# 判断一个数是否为素数
num = int(input("请输入一个正整数:"))

# 处理特殊情况
if num < 2:
    print(f"{num}不是素数")
elif num == 2:
    print(f"{num}是素数")
elif num % 2 == 0:
    print(f"{num}不是素数(能被2整除)")
else:
    # 判断从3到num-1是否有能整除的数
    is_prime = True  # 假设是素数

    for i in range(3, num):
        if num % i == 0:
            is_prime = False
            print(f"{num}不是素数(能被{i}整除)")
            break  # 找到一个因数就够了

    # 如果循环正常结束,说明没有找到因数
    if is_prime:
        print(f"{num}是素数!")

运行示例:

请输入一个正整数:17
17是素数!
请输入一个正整数:15
15不是素数(能被3整除)

进阶:打印100以内的所有素数:

# 打印100以内的所有素数
print("100以内的素数:")

for num in range(2, 101):
    if num == 2:
        print(num, end=" ")
        continue

    if num % 2 == 0:
        continue

    # 判断是否为素数
    is_prime = True
    for i in range(3, num):
        if num % i == 0:
            is_prime = False
            break

    if is_prime:
        print(num, end=" ")

输出结果:

100以内的素数:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

给家长的小贴士:

  • 素数判断是一个经典算法问题
  • 可以优化:只需要判断到√n,不需要到n-1
  • 这是密码学的基础( RSA加密算法用到素数)
  • 可以和孩子讨论为什么素数很重要

用循环简化Turtle图形

还记得第6章我们用Turtle画图形吗?当时我们用顺序语句,每个边都要写一遍代码。现在学完循环后,我们可以大大简化这些代码!

回顾:第6章的画图代码

在第6章,我们画正方形时是这样的:

import turtle

t = turtle.Turtle()
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)

turtle.done()

问题:这段代码重复了4次相同的内容!如果我们想画正六边形、正八边形,代码会变得很长。

用循环改进:画正n边形

现在我们可以用循环来简化:

import turtle

t = turtle.Turtle()

# 画正方形(4条边)
for i in range(4):
    t.forward(100)
    t.left(90)

turtle.done()

优势

  • 代码从8行减少到3行!
  • 如果要画正六边形,只需要改数字:
    for i in range(6):
        t.forward(100)
        t.left(60)  # 360 ÷ 6 = 60度
    

👨‍🏫 给家长的Tips

这是一个绝佳的教学时机!

  • 让孩子对比两种写法的代码行数
  • 问孩子:“哪种写法更简单?如果我要画正12边形呢?”
  • 引导孩子理解:循环就是让计算机帮我们重复做相同的事
  • 强调:程序员很“懒“,总在想办法让代码更简洁

练习1:改写长方形画法

第6章的代码(顺序语句):

length = 150
width = 80

t.forward(length)
t.left(90)
t.forward(width)
t.left(90)
t.forward(length)
t.left(90)
t.forward(width)
t.left(90)

用循环改进

length = 150
width = 80

for i in range(2):  # 重复2次
    t.forward(length)
    t.left(90)
    t.forward(width)
    t.left(90)

代码从10行减少到5行!

练习2:改写五边形

第6章的代码

side = 100
angle = 360 / 5  # 72度

t.forward(side)
t.left(angle)
t.forward(side)
t.left(angle)
t.forward(side)
t.left(angle)
t.forward(side)
t.left(angle)
t.forward(side)
t.left(angle)

用循环改进

side = 100
angle = 360 / 5

for i in range(5):
    t.forward(side)
    t.left(angle)

代码从11行减少到3行!

练习3:改写五角星

第6章的代码

t.pencolor("gold")
t.fillcolor("orange")
t.begin_fill()

t.forward(200)
t.right(144)
t.forward(200)
t.right(144)
t.forward(200)
t.right(144)
t.forward(200)
t.right(144)
t.forward(200)
t.right(144)

t.end_fill()

用循环改进

t.pencolor("gold")
t.fillcolor("orange")
t.begin_fill()

for i in range(5):
    t.forward(200)
    t.right(144)

t.end_fill()

挑战项目:用循环画复杂图形

现在你掌握了循环,可以尝试画更复杂的图形了!

项目1:彩色多边形螺旋

import turtle

t = turtle.Turtle()
t.speed(0)  # 最快速度

colors = ["red", "orange", "yellow", "green", "blue", "purple"]

# 画36个逐渐变大的正方形
for i in range(36):
    t.pencolor(colors[i % 6])  # 循环使用6种颜色
    t.forward(50 + i * 5)      # 每次边长增加
    t.left(90)                 # 转向
    t.forward(50 + i * 5)
    t.left(90)
    t.forward(50 + i * 5)
    t.left(90)
    t.forward(50 + i * 5)
    t.left(90)
    t.left(10)  # 每次多转10度,形成螺旋

turtle.done()

项目2:花朵(第6章留下的挑战)

在第6章,我们说花朵练习要等学完循环再做。现在可以了!

import turtle

t = turtle.Turtle()
t.speed(10)

colors = ["red", "orange", "yellow", "pink", "purple", "blue"]

# 画6个花瓣
for i in range(6):
    t.color(colors[i])
    t.begin_fill()

    # 画椭圆(通过多次小角度转向实现)
    for j in range(36):
        t.forward(20)
        t.left(10)
    t.left(60)  # 转到下一个花瓣

    t.end_fill()

# 画花心
t.penup()
t.goto(0, -10)
t.pendown()
t.color("green")
t.begin_fill()
t.circle(20)
t.end_fill()

turtle.done()

👨‍🏫 给家长的Tips

这个花朵项目使用了嵌套循环

  • 外层循环:画6个花瓣
  • 内层循环:每个花瓣由36条短线段组成
  • 这是循环的高级应用,让孩子看到循环的威力
  • 可以让孩子修改参数(如花瓣数量、颜色),观察变化

练习4:创意画图

用循环发挥你的创意!

想法

  • 用循环画一排五角星(10个,位置不同)
  • 用循环画同心圆(10个圆,半径逐渐增大)
  • 用循环画一个“图案生成器“(旋转对称图形)

示例:旋转的五角星

import turtle

t = turtle.Turtle()
t.speed(0)

# 画12个旋转的五角星
for i in range(12):
    t.pencolor("gold")
    t.begin_fill()

    for j in range(5):
        t.forward(100)
        t.right(144)

    t.end_fill()
    t.right(30)  # 旋转30度(360 ÷ 12 = 30)

turtle.done()

总结:循环的威力

通过对比第6章和现在的代码,我们发现:

图形第6章(顺序语句)现在(循环)减少行数
正方形8行3行减少5行
五边形11行3行减少8行
五角星11行3行减少8行
花朵不现实15行不可想象

循环的优势

  1. 代码更短:从几十行减少到几行
  2. 更易修改:想画正12边形?只改一个数字
  3. 更易阅读:代码意图更清晰
  4. 减少错误:不用重复写相同的代码

👨‍🏫 给家长的Tips

这个对比非常重要!

  • 让孩子真切感受到循环的价值
  • 回顾第6章时留下的“伏笔“,现在终于“解密“了
  • 告诉孩子:“这就是为什么我们要学循环——让代码更简洁、更强大”
  • 鼓励孩子用循环改写之前的所有画图代码

变量的作用域

在循环中定义的变量,在循环外面可以使用吗?

示例:循环变量的作用域

# 循环变量的作用域
for i in range(1, 6):
    print(f"循环内:i = {i}")

print(f"循环外:i = {i}")  # 可以访问i!

# 但是,如果循环一次都没执行呢?
for j in range(0):  # range(0)是空的
    print(f"循环内:j = {j}")

# print(f"循环外:j = {j}")  # 错误!j不存在

输出结果:

循环内:i = 1
循环内:i = 2
循环内:i = 3
循环内:i = 4
循环内:i = 5
循环外:i = 5

给家长的小贴士:

  • Python中,for循环的变量在循环外仍然可以访问
  • 但是,如果循环一次都没执行,变量就不存在
  • 建议:在循环外定义好所有需要的变量
  • 这样代码更清晰,不容易出错

循环和分支互相嵌套

示例:求和或求乘积

题目:要求用户输入一个正整数n,然后问用户计算求和还是求乘积。如果用户输入求和,则输出1+2+…+n的结果;用户输入求乘积,则输出1×2×…×n的结果。

👉 点击查看答案
# 求和或求乘积
n = int(input("请输入一个正整数n:"))
choice = input("请选择计算方式(求和/求乘积):")

if choice == "求和":
    # 计算求和
    total = 0
    for i in range(1, n + 1):
        total = total + i
    print(f"1+2+...+{n} = {total}")

elif choice == "求乘积":
    # 计算求乘积
    product = 1
    for i in range(1, n + 1):
        product = product * i
    print(f"1×2×...×{n} = {product}")

else:
    print("Error!!! 输入不正确")

运行示例:

请输入一个正整数n:5
请选择计算方式(求和/求乘积):求和
1+2+...+5 = 15
请输入一个正整数n:5
请选择计算方式(求和/求乘积):求乘积
1×2×...×5 = 120

常见错误和调试

错误1:忘记更新循环变量

# 错误:死循环
count = 0
while count < 5:
    print(count)
    # 忘记了 count = count + 1

# 正确
count = 0
while count < 5:
    print(count)
    count = count + 1  # 更新循环变量!

错误2:循环条件错误

# 错误:永远不会执行
count = 5
while count > 10:  # 5 > 10是False
    print(count)

# 正确
count = 5
while count < 10:  # 5 < 10是True
    print(count)
    count = count + 1

错误3:off-by-one错误

# 常见错误:想打印1到10,但只打印到9
for i in range(1, 10):  # 错误!range(1, 10)是1到9
    print(i)

# 正确
for i in range(1, 11):  # 正确!range(1, 11)是1到10
    print(i)

错误4:缩进错误

# 错误:缩进不一致
for i in range(5):
  print(i)  # 2个空格
    print(i * 2)  # 4个空格

# 正确:缩进一致
for i in range(5):
    print(i)
    print(i * 2)

调试技巧

  1. 使用print语句:
for i in range(1, 6):
    print(f"调试:i = {i}")  # 调试信息
    total = total + i
    print(f"调试:total = {total}")  # 调试信息
  1. 画表格追踪变量: | 循环次数 | i | total | |———|—|—––| | 第1次 | 1 | 1 | | 第2次 | 2 | 3 | | 第3次 | 3 | 6 |

  2. 简化问题:

    • 先用小的数字测试(比如1到5,而不是1到100)
    • 确认逻辑正确后再扩大范围
  3. 检查边界条件:

    • 0会怎样?
    • 1会怎样?
    • 最大值会怎样?

给家长的小贴士:

  • 教孩子学会看错误信息
  • 鼓励孩子用print调试
  • 建议先在纸上画出执行过程
  • 强调“从小到大“测试策略

章节小结

我们学到了什么

  1. 循环的概念:重复执行某些操作
  2. while循环:条件循环,适合不知道循环次数的情况
  3. for循环:计数循环,适合知道循环次数的情况
  4. range()函数:生成数字序列
  5. break和continue:控制循环的执行
  6. 循环嵌套:循环中的循环
  7. 常见模式:计数器、累加器、累乘器、最大值查找

重要语法回顾

循环类型语法用途
whilewhile 条件:条件循环
forfor i in range():计数循环
breakbreak跳出循环
continuecontinue跳过本次

循环的三个要素

  1. 初始值:循环变量的起点
  2. 条件:判断是否继续循环
  3. 更新:循环变量如何变化

编程技巧

  1. 避免死循环:记得更新循环变量
  2. 注意边界:range(1, 5)是1到4,不包括5
  3. 缩进正确:循环体要统一缩进
  4. 简化测试:先用小数字测试,确认逻辑后再扩大
  5. 调试方法:用print输出中间结果

下一步

在下一章(第9章),我们将学习流程图,用图形化的方式表示程序的执行流程。流程图能帮助我们更好地理解程序的逻辑!

挑战练习

  1. 必做:

    • 用for循环计算1到1000的和
    • 打印所有三位数中,个位、十位、百位都相同的数字(如111, 222, 333)
    • 用while循环实现:输入密码,直到输入正确为止
  2. 选做:

    • 打印“斐波那契数列“的前20项(1, 1, 2, 3, 5, 8, 13, …)
    • 计算阶乘:n! = 1×2×3×…×n
    • 打印“杨辉三角“的前10行
  3. 挑战:

    • 用循环实现“九九乘法表“的倒三角版本
    • 找出所有“完全数“:一个数等于它的所有真因数之和(如6=1+2+3)
    • 实现“猜数字“游戏,并在猜测次数上设置难度级别
  4. 综合项目:

    • 写一个简单的计算器,可以循环计算,直到用户选择退出
    • 写一个成绩管理系统,可以录入、查询、统计成绩
    • 用Turtle库和循环,画一个螺旋图形

加油!你已经掌握了让程序重复工作的能力!🎉

流程图

引言

在前面的章节中,我们学习了三种基本的程序控制结构:

  • 顺序语句(第6章):程序按从上到下的顺序执行
  • 条件语句(第7章):根据条件选择执行不同的代码
  • 循环语句(第8章):重复执行某些代码

这些结构让程序能够处理各种复杂的情况。但是,当程序变得比较复杂时,仅靠阅读代码来理解逻辑可能会有些困难。

有没有一种方法,可以用图形的方式来表示程序的执行流程呢?

答案是:有!这就是流程图。

流程图(Flowchart)是用图形符号来表示程序执行流程的图示方法。它能帮助我们:

  • 更清晰地理解程序的逻辑
  • 在编写代码之前设计程序
  • 与他人交流程序的设计思路
  • 调试和优化程序

这一章,我们将学习:

  • 流程图的基本符号
  • 如何用流程图表示顺序、条件和循环
  • 如何从流程图写出代码
  • 如何从代码画出流程图
  • 什么是算法 💡
  • 用流程图表示计算机的工作过程 💡

💡 什么是算法?

在学流程图之前,我们先了解一个重要的概念——算法(Algorithm)。

算法就是解决问题的步骤

生活中的例子 🍳 比如“做番茄炒蛋“的算法:

  1. 准备食材:番茄2个、鸡蛋3个
  2. 处理食材:番茄切块、鸡蛋打散
  3. 炒鸡蛋:热锅加油,倒入鸡蛋炒熟
  4. 炒番茄:加入番茄翻炒
  5. 调味:加盐,翻炒均匀
  6. 装盘:盛到盘子里

**这就是一个算法!**它告诉我们:

  • 从哪里开始(准备食材)
  • 每一步做什么(炒鸡蛋、炒番茄)
  • 什么时候结束(装盘)

编程中的算法 💻 我们前面学过的程序也是算法:

# 计算1到n的和(算法)
n = int(input("请输入n:"))  # 步骤1:输入n
total = 0                     # 步骤2:初始化总和
i = 1                         # 步骤3:初始化计数器

while i <= n:                  # 步骤4:循环累加
    total = total + i
    i = i + 1

print(total)                   # 步骤5:输出结果

算法的三个特征:

  1. 有输入:比如上面的程序输入n
  2. 有步骤:一步一步执行(初始化→循环→输出)
  3. 有输出:最后输出总和

为什么流程图和算法是一回事? 🤔

  • 算法:用文字描述解决问题的步骤
  • 流程图:用图形描述解决问题的步骤

它们描述的是同一件事,只是表达方式不同!

👨‍🏫 给家长的Tips

知识点补充(针对家长):

  • 算法是计算机科学的核心概念
  • 一个算法可以用多种方式表示:
    • 自然语言(文字描述)
    • 流程图(图形表示)
    • 伪代码(介于自然语言和代码之间)
    • 编程语言(Python、C++等)
  • 流程图是算法的一种“可视化“表示

如何给孩子讲解:

  • 用“做菜的菜谱“类比算法
  • 菜谱可以用文字写,也可以画成图示
  • 流程图就是程序的“图示版菜谱“
  • 强调:算法是“想法“,流程图是“画出来的想法“

💡 用流程图表示计算机的工作过程

在第6章,我们学习了CPU是如何执行指令的:

  • 取指令译码执行重复

现在我们用流程图来表示CPU的工作过程:

CPU的指令执行循环

flowchart TD
    Start([开始]) --> Init[PC = 0<br/>PC=程序计数器]
    Init --> Fetch{是否还有指令?}
    Fetch -->|是| FetchStep[从内存读取指令]
    FetchStep --> Decode[译码:理解指令含义]
    Decode --> Execute[执行指令]
    Execute --> UpdatePC[PC = PC + 1<br/>指向下一条指令]
    UpdatePC --> Fetch
    Fetch -->|否| End([结束])

流程图解释:

  1. 开始:启动计算机
  2. 初始化:PC(程序计数器)设为0,指向第一条指令
  3. 判断:是否还有指令要执行?
  4. 取指:从内存读取当前指令
  5. 译码:CPU理解这条指令要做什么
  6. 执行:CPU执行指令(加法、减法、判断等)
  7. 更新PC:PC加1,指向下一条指令
  8. 返回:回到第3步,继续执行
  9. 结束:程序执行完毕

这正好解释了为什么计算机这么快!

  • 这个循环每秒可以执行几十亿次!
  • 每次循环(取指→译码→执行)只需要纳秒(1纳秒=0.000000001秒)

👨‍🏫 给家长的Tips

知识点补充(针对家长):

  • CPU的工作过程就是一个“无限循环“
  • 术语叫“指令循环“(Instruction Cycle)
  • 每个时钟周期执行一次循环
  • 3GHz CPU = 每秒30亿次指令循环

如何给孩子讲解:

  • 用“传送带“类比:
    • CPU就像一个工人
    • 指令就像传送带上的零件
    • 工人不断拿起零件(取指)
    • 理解零件怎么用(译码)
    • 处理零件(执行)
    • 然后拿起下一个零件…
  • 强调:这个循环比人类快亿万倍

数学小计算 🧮:

  • 如果CPU主频是3GHz(每秒30亿个周期)
  • 执行1亿条简单指令需要:100,000,000 ÷ 3,000,000,000 = 0.03秒!
  • 这就是为什么计算机适合做重复性工作!

程序执行流程图

让我们用一个简单的程序来看CPU是如何执行的:

代码:

# 计算2的3次方
result = 1
result = result * 2  # 第1次乘法
result = result * 2  # 第2次乘法
result = result * 2  # 第3次乘法
print(result)

CPU执行过程流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ┌──────────────────┐
   │ PC = 0 (指向第1行)│
   └────┬─────────────┘
        ↓
      ↑
     ╱ ╲
    ╱   ╲
   ╱     ╲
  ╱PC<4?  ╲───┐ ← 代码共4行
 ╱ (判断)  ╲   │
   ╱   ╲       │
 是    否      │
 ╱      ╲      │
 ↓        ╲     │
┌───────┐  ╲    │
│执行指令│   ╲   │
│PC指向的│    ↓  │
└───┬───┘ ┌─────┘
    │     │
    ↓     │
┌──────────┐│
│ PC = PC+1││ → 指向下一条指令
└─────┬────┘│
      │     │
      └─────┘
           ↓
   ┌─────────────────╲
  ╱ 输出: result     ╲
 ╱────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

执行过程详解:

  1. PC=0:执行第1行 result = 1,然后PC变为1
  2. PC=1:执行第2行 result = result * 2,然后PC变为2
  3. PC=2:执行第3行 result = result * 2,然后PC变为3
  4. PC=3:执行第4行 result = result * 2,然后PC变为4
  5. PC=4:PC>=4,退出循环,执行下一条指令(print)
  6. 输出:打印result的值(8)

👨‍🏫 给家长的Tips

重要概念:

  • 程序计数器(PC):CPU中的“书签“,记录执行到哪一行
  • PC从0开始,每执行一行就+1
  • 当PC超出程序行数时,程序结束
  • 这就是“顺序执行“的硬件原理!

如何给孩子讲解:

  • 用“看书“类比:
    • PC就像你的“手指“,指到哪行就读哪行
    • 读完后,手指移到下一行(PC+1)
    • 读到最后一页,书就结束了
  • 强调:CPU每秒可以做这样的“翻页“几十亿次!

流程图与算法的关系

现在我们理解了:

  • 算法 = 解决问题的步骤(文字描述)
  • 流程图 = 算法的图形表示
  • 程序 = 算法的代码实现
  • CPU执行 = 按照流程图的步骤执行

四种表示方式的对比:

表示方式例子优点缺点
算法(文字)“1.输入n 2.初始化sum=0 3.循环累加 4.输出”易理解,不要求技术不够精确,有歧义
流程图(图形)用符号和箭头表示直观清晰,逻辑一目了然复杂程序图画起来麻烦
程序(代码)for i in range(n+1): sum+=i精确,计算机能执行需要学习编程语法
CPU执行取指→译码→执行的循环计算机真正的工作方式过于底层,不适合设计算法

👨‍🏫 给家长的Tips

教学建议:

  • 告诉孩子:程序员写代码时,通常的流程是:
    1. 先想算法(用文字描述思路)
    2. 画流程图(把思路图形化)
    3. 写代码(把流程图翻译成Python)
  • 这就是为什么我们要学流程图——它是“想法“和“代码“之间的桥梁!
  • 鼓励孩子养成“先画流程图,再写代码“的习惯

流程图的基本符号

流程图使用不同的图形符号来表示不同的操作。让我们先认识这些符号。

1. 开始/结束符号(圆角矩形)

flowchart TD
    Start([开始])
    End([结束])

作用:表示程序的开始或结束

特点:

  • 通常使用圆角矩形或椭圆形
  • “开始“符号只有一个向外的箭头
  • “结束“符号只有一个向内的箭头

2. 输入/输出符号(平行四边形)

flowchart TD
    Input[/输入n/]
    Output[/输出结果/]

作用:表示输入(input)或输出(print)操作

示例:

  • n = int(input("请输入n:"))
  • print("结果是:", sum)

3. 处理符号(矩形)

flowchart TD
    Process[sum = 0]

作用:表示计算、赋值等处理操作

示例:

  • sum = sum + i
  • i = i + 1
  • area = length * width

4. 判断符号(菱形)

flowchart TD
    Decision{i <= n?}
    Decision-->|是| TruePath
    Decision-->|否| FalsePath

作用:表示条件判断

特点:

  • 有两个出口:True(是)和False(否)
  • 用于if语句和循环的条件

5. 流程线(箭头)

flowchart LR
    A[开始] --> B[下一步]

作用:表示程序的执行方向

符号总结表

符号名称作用Python示例
开始/结束程序的开始或结束-
╱ ╲输入/输出input()或print()input(), print()
┌─┐处理计算、赋值sum = 0, i = i + 1
判断条件判断if, while, for
流程线执行方向-

给家长的小贴士:

  • 建议让孩子把这些符号画在卡片上
  • 可以玩“配对游戏“:将符号与对应的代码配对
  • 强调每个符号的形状和用途的关联
  • 菱形有尖角,像路口需要选择方向
  • 平行四边形倾斜,像数据进进出出

用流程图表示顺序语句

顺序语句是最简单的程序结构,按照从上到下的顺序执行。

示例1:计算长方形面积

代码:

# 计算长方形面积
length = 5
width = 3
area = length * width
print(area)

流程图:

flowchart TD
    Start([开始]) --> A[length = 5]
    A --> B[width = 3]
    B --> C[area = length * width]
    C --> D[/输出: area/]
    D --> End([结束])

要点:

  • 箭头始终向下,表示顺序执行
  • 每个步骤按顺序完成后再进行下一个
  • 没有分支,没有循环

示例2:带输入的顺序程序

代码:

# 输入长和宽,计算面积
length = int(input("请输入长度:"))
width = int(input("请输入宽度:"))
area = length * width
print(f"面积是:{area}")

流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ╱─────────────────╲
  ╱ 输入: length     ╲
 ╱─────────────────────╲
        ↓
   ╱─────────────────╲
  ╱  输入: width     ╲
 ╱─────────────────────╲
        ↓
   ┌──────────────────┐
   │ area = l * w    │
   └────┬─────────────┘
        ↓
   ╱─────────────────╲
  ╱ 输出: area       ╲
 ╱─────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

给家长的小贴士:

  • 顺序语句的流程图最简单:一条直线向下
  • 可以让孩子“用手指走一遍“流程图
  • 强调:一步接一步,不能跳跃
  • 可以用生活中的例子类比:做菜的步骤、早晨起床的流程

练习1:画出温度转换程序的流程图

题目:画流程图表示下面的程序。

# 温度转换:摄氏度转华氏度
celsius = float(input("请输入摄氏度:"))
fahrenheit = celsius * 9 / 5 + 32
print(f"华氏度是:{fahrenheit}")
👉 点击查看答案
   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ╱────────────────────╲
  ╱ 输入: celsius       ╲
 ╱────────────────────────╲
        ↓
   ┌──────────────────────────┐
   │ f = c * 9 / 5 + 32       │
   └────┬─────────────────────┘
        ↓
   ╱────────────────────╲
  ╱ 输出: fahrenheit    ╲
 ╱────────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

用流程图表示条件语句

条件语句需要根据条件判断选择执行不同的代码分支,在流程图中用菱形表示。

示例1:简单的if语句

代码:

# 判断数字是否为正数
num = int(input("请输入一个数字:"))

if num > 0:
    print("这是正数")

print("程序结束")

流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ╱─────────────────╲
  ╱  输入: num      ╲
 ╱─────────────────────╲
        ↓
      num > 0?
     ╱    ╲
   是   否
   ╱      ╲
   ↓        ╲
┌────────┐   ╲
│输出"正数"│   ╲
└────┬────┘   ╲
     ↓        ╲
     └────┬────┘
          ↓
   ╱─────────────────╲
  ╱ 输出"程序结束"   ╲
 ╱─────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

要点:

  • 菱形表示判断条件
  • “是”(True)分支和“否“(False)分支
  • 两个分支最终汇合到一起

示例2:if-else语句

代码:

# 判断奇偶数
num = int(input("请输入一个数字:"))

if num % 2 == 0:
    print("这是偶数")
else:
    print("这是奇数")

print("程序结束")

流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ╱─────────────────╲
  ╱  输入: num      ╲
 ╱─────────────────────╲
        ↓
    num % 2 == 0?
     ╱      ╲
   是       否
   ╱        ╲
   ↓          ↓
┌────────┐ ┌────────┐
│输出"偶数"│ │输出"奇数"│
└────┬────┘ └────┬───┘
     └────┬────┘
          ↓
   ╱─────────────────╲
  ╱ 输出"程序结束"   ╲
 ╱─────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

要点:

  • if-else的两个分支是对称的
  • 两个分支都会执行完后汇合

示例3:if-elif-else语句

代码:

# 成绩等级判断
score = int(input("请输入成绩:"))

if score >= 90:
    print("优秀")
elif score >= 80:
    print("良好")
elif score >= 60:
    print("及格")
else:
    print("不及格")

print("程序结束")

流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ╱─────────────────╲
  ╱  输入: score    ╲
 ╱─────────────────────╲
        ↓
   score >= 90?
     ╱      ╲
   是       否
   ╱         ╲
   ↓           ↓
┌────────┐  score >= 80?
│输出"优秀"│   ╱      ╲
└────┬───┘ 是        否
     └─┬────┘        ╲
       ↓             ╲
    ┌────────┐  score >= 60?
    │输出"良好"│   ╱      ╲
    └────┬───┘ 是        否
         └─┬────┘        ╲
           ↓             ╲
        ┌────────┐  ┌────────┐
        │输出"及格"│ │输出"不及格"│
        └────┬───┘ └────┬───┘
             └────┬────┘
                  ↓
        ╱─────────────────╲
       ╱ 输出"程序结束"   ╲
      ╱─────────────────────╲
              ↓
          ┌─────────┐
          │  结束   │
          └─────────┘

要点:

  • 多个条件依次判断
  • 一旦某个条件为True,执行对应分支后不再判断其他条件
  • 所有条件都不满足时,执行else分支

给家长的小贴士:

  • 条件语句的流程图像一棵“树“
  • 强调:菱形有两个出口
  • 让孩子用手指沿着不同的路径走一遍
  • 可以画在纸上,用不同颜色的笔标记不同的路径
  • 对于if-elif-else,强调“一旦满足就不再判断“

练习2:画出绝对值程序的流程图

题目:画流程图表示下面的程序。

# 计算绝对值
num = int(input("请输入一个数字:"))

if num < 0:
    num = -num

print(f"绝对值是:{num}")
👉 点击查看答案
   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ╱─────────────────╲
  ╱  输入: num      ╲
 ╱─────────────────────╲
        ↓
      num < 0?
     ╱      ╲
   是       否
   ╱        ╲
   ↓          ╲
┌──────────┐   ╲
│ num = -num│   ╲
└────┬─────┘   ╲
     └────┬────┘
          ↓
   ╱────────────────────╲
  ╱ 输出: num (绝对值)  ╲
 ╱────────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

用流程图表示循环语句

循环语句的流程图与条件语句类似,也用菱形表示判断,但有一个重要的区别:循环有返回箭头,形成回路

示例1:while循环

代码:

# 打印1到5
count = 1

while count <= 5:
    print(count)
    count = count + 1

print("循环结束")

流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ┌──────────┐
   │count = 1 │
   └────┬─────┘
        ↓
      ↑
     ╱ ╲
    ╱   ╲
   ╱     ╲
  ╱count<=5?╲───────┐
 ╱   (判断)  ╲       │
     ╱   ╲          │
   是     否         │
   ╱       ╲         │
   ↓         ╲       │
┌────────┐   ╲       │
│输出count│   ↓      │
└───┬────┘   ┌─────────┐
    ↓        │count = c │
    │        │  + 1     │
    │        └────┬────┘
    │             │
    └─────────────┘
          ↓
   ╱─────────────────╲
  ╱ 输出"循环结束"   ╲
 ╱─────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

要点:

  • while循环的三个要素:
    1. 初始值:count = 1
    2. 条件:count <= 5
    3. 更新:count = count + 1
  • 当条件为True时,执行循环体,然后返回再次判断条件
  • 当条件为False时,退出循环

示例2:for循环(等价while)

代码:

# 用for循环打印1到5
for count in range(1, 6):
    print(count)

print("循环结束")

流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ┌──────────────┐
   │count = range(1,6)│
   └────┬─────────┘
        ↓
      ↑
     ╱ ╲
    ╱   ╲
   ╱     ╲
  ╱count<=5?╲───────┐
 ╱   (判断)  ╲       │
     ╱   ╲          │
   是     否         │
   ╱       ╲         │
   ↓         ╲       │
┌────────┐   ╲       │
│输出count│   ↓      │
└───┬────┘   ┌──────────┐
    ↓        │count自动+1│
    │        └────┬─────┘
    │             │
    └─────────────┘
          ↓
   ╱─────────────────╲
  ╱ 输出"循环结束"   ╲
 ╱─────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

要点:

  • for循环的菱形判断条件来自range()
  • for循环会自动更新循环变量
  • 流程图与while类似,但更新是自动的

示例3:带break的循环

代码:

# 找第一个能被7整除的数
for i in range(1, 100):
    if i % 7 == 0:
        print(f"找到了:{i}")
        break

print("程序结束")

流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ┌──────────────┐
   │  i = range   │
   │   (1, 100)   │
   └────┬─────────┘
        ↓
      ↑
     ╱ ╲
    ╱   ╲
   ╱     ╲
  ╱i < 100? ╲─────┐
 ╱   (判断)  ╲     │
     ╱   ╲        │
   是     否       │
   ╱       ╲      │
   ↓         ╲    │
i % 7 == 0?       │
 ╱      ╲         │
是       否        │
 ╱        ╲       │
↓          ╲      │
┌────────┐   ╲    │
│输出: i │    ╲   │
│  break│────→╲──┘ ←─ break直接跳出
└────────┘     ╲    循环
              ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

要点:

  • break会立即跳出循环
  • 不再返回判断条件
  • 直接执行循环后的语句

示例4:带continue的循环

代码:

# 打印1到10中的奇数
for i in range(1, 11):
    if i % 2 == 0:
        continue  # 跳过偶数
    print(i)

print("程序结束")

流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ┌──────────────┐
   │  i = range   │
   │   (1, 11)    │
   └────┬─────────┘
        ↓
      ↑
     ╱ ╲
    ╱   ╲
   ╱     ╲
  ╱i < 11? ╲──────┐
 ╱   (判断)  ╲     │
     ╱   ╲        │
   是     否       │
   ╱       ╲      │
   ↓         ╲    │
i % 2 == 0?        │
 ╱      ╲         │
是       否        │
 ╱        ╲       │
↓          ↓      │
│continue┐ ┌──────┤
│(跳过)  │ │输出 i│
└────────┘ └──┬───┘
   ↑          │
   │          │
   └──────────┘
        ↓
   ╱─────────────────╲
  ╱ 输出"程序结束"   ╲
 ╱─────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

要点:

  • continue跳过本次循环的剩余代码
  • 立即返回判断条件,进入下一次循环
  • 不像break那样退出循环

给家长的小贴士:

  • 循环流程图的关键是“回路“
  • 强调:条件满足时“返回“再次判断
  • 可以用“跑道“类比:跑完一圈回到起点
  • break是“离开跑道“,continue是“重跑一圈“
  • 让孩子用手指沿着箭头走,体验循环

循环vs条件语句:流程图的区别

特征条件语句循环语句
菱形判断
两个分支是/否是/否
返回箭头
执行次数一次可能多次
目的选择分支重复执行

总结:

  • 条件语句:菱形→分支→汇合→向下(无返回)
  • 循环语句:菱形→分支→返回(有返回)

练习3:画出累加程序的流程图

题目:画流程图表示下面的程序。

# 计算1到n的和
n = int(input("请输入n:"))

total = 0
i = 1

while i <= n:
    total = total + i
    i = i + 1

print(f"1到{n}的和是:{total}")
👉 点击查看答案
   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ╱─────────────────╲
  ╱  输入: n        ╲
 ╱─────────────────────╲
        ↓
   ┌──────────┐
   │ total=0 │
   │   i=1   │
   └────┬─────┘
        ↓
      ↑
     ╱ ╲
    ╱   ╲
   ╱     ╲
  ╱ i <= n?╲──────┐
 ╱   (判断)  ╲     │
     ╱   ╲        │
   是     否       │
   ╱       ╲      │
   ↓         ╲    │
┌────────────┐  ╲  │
│total=total+i│   ↓ │
│  i = i+1   │  ┌──┘
└─────┬──────┘
      │
      └───────────┘
            ↓
   ╱────────────────────╲
  ╱ 输出: total (和)     ╲
 ╱────────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

综合示例:嵌套结构的流程图

让我们看一个更复杂的例子,包含条件语句和循环语句的嵌套。

示例:计算能被3整除的数字之和

代码:

# 计算1到n中能被3整除的数字之和
n = int(input("请输入n:"))

sum = 0
i = 1

while i <= n:
    if i % 3 == 0:
        sum = sum + i
    i = i + 1

print(f"1到{n}中能被3整除的数字之和是:{sum}")

流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ╱─────────────────╲
  ╱  输入: n        ╲
 ╱─────────────────────╲
        ↓
   ┌──────────┐
   │ sum = 0 │
   │  i = 1  │
   └────┬─────┘
        ↓
      ↑
     ╱ ╲
    ╱   ╲
   ╱     ╲
  ╱ i <= n?╲───────────┐
 ╱   (判断)  ╲          │
     ╱   ╲             │
   是     否            │
   ╱       ╲            │
   ↓         ╲          │
 i % 3 == 0?            │
   ╱   ╲               │
 是     否              │
  ╱       ╲             │
 ↓         ╲            │
┌────────┐   ╲          │
│sum=sum+i│    ╲        │
└────┬───┘     ╲        │
     └───┬─────╲        │
         ↓      ╲       │
    ┌─────────┐   ╲     │
    │ i = i+1 │    ↓    │
    └────┬────┘   ┌────┘
         │
         └───────────┘
               ↓
   ╱───────────────────────╲
  ╱ 输出: sum              ╲
 ╱───────────────────────────╲
         ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

要点:

  • 外层循环:while(i <= n)
  • 内层条件:if(i % 3 == 0)
  • 内层条件在循环内部
  • 不管是否满足内层条件,都要执行i = i + 1

给家长的小贴士:

  • 嵌套结构的流程图可以分层理解
  • 先看外层结构(while循环)
  • 再看内层结构(if条件)
  • 让孩子用不同颜色的笔标记不同的层级
  • 强调:缩进在代码中对应嵌套在流程图中

从流程图写代码

学会了画流程图,现在我们来学习如何从流程图写出代码。

示例:求最大值

流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ╱─────────────────╲
  ╱ 输入: a, b, c   ╲
 ╱─────────────────────╲
        ↓
      a >= b?
     ╱      ╲
   是       否
   ╱        ╲
   ↓          ↓
 max = a    max = b
   └────┬────┘
        ↓
    max >= c?
     ╱      ╲
   是       否
   ╱        ╲
   ↓          ↓
(不变)     max = c
   └────┬────┘
        ↓
   ╱─────────────────╲
  ╱ 输出: max       ╲
 ╱─────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

步骤:

  1. 开始:程序开始
  2. 输入:a = int(input()), b = int(input()), c = int(input())
  3. 第一个判断(a >= b):
    if a >= b:
        max = a
    else:
        max = b
    
  4. 第二个判断(max >= c):
    if max >= c:
        # max不变
        pass
    else:
        max = c
    
  5. 输出:print(max)

完整代码:

# 求三个数的最大值
a = int(input("请输入第一个数:"))
b = int(input("请输入第二个数:"))
c = int(input("请输入第三个数:"))

# 第一步:比较a和b
if a >= b:
    max = a
else:
    max = b

# 第二步:比较max和c
if max >= c:
    # max已经是最大的,不需要做任何事
    pass
else:
    max = c

print(f"最大的数是:{max}")

或者更简洁的写法:

# 求三个数的最大值(简洁版)
a = int(input("请输入第一个数:"))
b = int(input("请输入第二个数:"))
c = int(input("请输入第三个数:"))

max = a  # 假设a最大

if b > max:  # 如果b更大
    max = b

if c > max:  # 如果c更大
    max = c

print(f"最大的数是:{max}")

给家长的小贴士:

  • 从流程图写代码的关键步骤:
    1. 识别符号类型(输入、输出、处理、判断)
    2. 按照箭头方向顺序转换
    3. 菱形转换为if语句
    4. 回路转换为while或for循环
  • 鼓励孩子先写“直译版“,再优化
  • 强调:流程图和代码要一一对应

练习4:从流程图写代码(累加)

流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ╱─────────────────╲
  ╱  输入: n        ╲
 ╱─────────────────────╲
        ↓
   ┌──────────┐
   │ sum = 0 │
   │  i = 1  │
   └────┬─────┘
        ↓
      ↑
     ╱ ╲
    ╱   ╲
   ╱     ╲
  ╱ i <= n?╲──────┐
 ╱   (判断)  ╲     │
     ╱   ╲        │
   是     否       │
   ╱       ╲      │
   ↓         ╲    │
┌──────────┐   ╲  │
│sum = sum+i│   ↓ │
│  i = i+1 │   ┌──┘
└─────┬────┘
      │
      └───────────┘
            ↓
   ╱─────────────────╲
  ╱ 输出: sum       ╲
 ╱─────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘
👉 点击查看答案

代码:

# 计算1到n的和
n = int(input("请输入n:"))

sum = 0
i = 1

while i <= n:
    sum = sum + i
    i = i + 1

print(f"1到{n}的和是:{sum}")

从代码画流程图

现在我们反过来,从代码画流程图。

示例:水仙花数

代码:

# 打印水仙花数
print("三位数的水仙花数:")

for num in range(100, 1000):
    # 分解百位、十位、个位
    hundreds = num // 100
    tens = (num // 10) % 10
    ones = num % 10

    # 计算各位数字的立方和
    sum_of_cubes = hundreds ** 3 + tens ** 3 + ones ** 3

    # 判断是否为水仙花数
    if sum_of_cubes == num:
        print(num)

print("查找完成!")

流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ╱─────────────────────╲
  ╱ 输出:"三位数的水仙花数"╲
 ╱─────────────────────────╲
        ↓
   ┌──────────────────┐
   │ num = range(100,│
   │      1000)      │
   └────┬─────────────┘
        ↓
      ↑
     ╱ ╲
    ╱   ╲
   ╱     ╲
  ╱num<1000?╲────────┐
 ╱   (判断)  ╲        │
     ╱   ╲           │
   是     否          │
   ╱       ╲          │
   ↓         ╲        │
┌─────────────┐  ╲     │
│h = num//100│   ↓    │
│t = (num//10)%10│  ┌──┘
│o = num % 10 │   │
└──────┬──────┘   │
       ↓          │
┌──────────────────┐
│sum = h³ + t³ + o³│
└──────┬───────────┘
       ↓
  sum == num?
    ╱    ╲
  是      否
  ╱        ╲
↓          ╲
┌────────┐   ╲
│输出num │    ╲
└────────┘     ╲
   └────┬──────┘
        ↓
   ╱─────────────────╲
  ╱ 输出"查找完成!"  ╲
 ╱─────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

练习5:画出猜数字游戏的流程图

题目:画流程图表示下面的猜数字游戏程序。

# 猜数字游戏
import random

target = random.randint(1, 100)
print("我想了一个1到100之间的数字,你来猜!")

guess = int(input("请输入你的猜测:"))

while guess != target:
    if guess < target:
        print("太小了!")
    else:
        print("太大了!")

    guess = int(input("请再次输入你的猜测:"))

print("恭喜你!猜对了!")
👉 点击查看答案
   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ┌──────────────────┐
   │生成随机数target  │
   │  (1到100)        │
   └────┬─────────────┘
        ↓
   ╱───────────────────────╲
  ╱ 输出:"我想了一个数字..."╲
 ╱───────────────────────────╲
        ↓
   ╱─────────────────╲
  ╱  输入: guess    ╲
 ╱─────────────────────╲
        ↓
      ↑
     ╱ ╲
    ╱   ╲
   ╱     ╲
  ╱guess!=target?╲─────┐
 ╱   (判断)      ╲      │
     ╱   ╲             │
   是     否            │
   ╱       ╲            │
   ↓         ╲          │
guess<target?           │
  ╱     ╲               │
 是       否             │
  ╱        ╲            │
↓          ↓            │
┌─────────┐ ┌─────────┐ │
│输出"太小"│ │输出"太大"│ │
└────┬────┘ └────┬────┘ │
     └────┬────┘         │
          ↓              │
   ╱─────────────────╲   │
  ╱  输入: guess    ╲   │
 ╱─────────────────────╲  │
          │              │
          └──────────────┘
                ↓
   ╱─────────────────────╲
  ╱ 输出"恭喜你!猜对了!" ╲
 ╱────────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

流程图练习:反向推理

现在让我们做一些有趣的练习:根据流程图填空,或者根据不完整的流程图写出完整的程序。

练习6:补全流程图

题目:下面的流程图中有一些空白,请根据程序逻辑补全。

程序:计算1到n中所有偶数的和

n = int(input("请输入n:"))

sum = 0
i = 1

while i <= n:
    if i % 2 == 0:  # 判断是否为偶数
        sum = sum + i
    i = i + 1

print(f"1到{n}中偶数的和是:{sum}")

不完整的流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ╱─────────────────╲
  ╱  输入: _____    ╲  ← 空白1
 ╱─────────────────────╲
        ↓
   ┌──────────┐
   │ sum = 0 │
   │  i = ___ │          ← 空白2
   └────┬─────┘
        ↓
      ↑
     ╱ ╲
    ╱   ╲
   ╱     ╲
  ╱ i <= n? ╲──────┐
 ╱   (判断)  ╲     │
     ╱   ╲        │
   是     否       │
   ╱       ╲      │
   ↓         ╲    │
 _________?       │  ← 空白3
   ╱   ╲         ╲
 是     否         ╲
  ╱        ╲       ╲
 ↓          ╲       ╲
┌────────┐    ╲      ╲
│sum = + i│     ↓     ╲
└────┬───┘   ┌──────┐  ╲
     └───────┤i = i+1│  ╲
             └──────┘  ╲
               └───────┘
                    ↓
   ╱────────────────────╲
  ╱ 输出: sum (偶数和)  ╲
 ╱───────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘
👉 点击查看答案

答案:

  • 空白1: n
  • 空白2: 1
  • 空白3: i % 2 == 0?

完整流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ╱─────────────────╲
  ╱  输入: n        ╲
 ╱─────────────────────╲
        ↓
   ┌──────────┐
   │ sum = 0 │
   │  i = 1  │
   └────┬─────┘
        ↓
      ↑
     ╱ ╲
    ╱   ╲
   ╱     ╲
  ╱ i <= n? ╲──────┐
 ╱   (判断)  ╲     │
     ╱   ╲        │
   是     否       │
   ╱       ╲      │
   ↓         ╲    │
 i%2==0?          ╲
   ╱   ╲         ╲
 是     否         ╲
  ╱        ╲       ╲
 ↓          ╲       ╲
┌────────┐    ╲      ╲
│sum=sum+i│     ↓     ╲
└────┬───┘   ┌──────┐  ╲
     └───────┤i = i+1│  ╲
             └──────┘  ╲
               └───────┘
                    ↓
   ╱────────────────────╲
  ╱ 输出: sum (偶数和)  ╲
 ╱───────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘

练习7:根据流程图写程序

题目:根据下面的流程图写出完整的程序。

流程图:

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ╱─────────────────╲
  ╱  输入: n        ╲
 ╱─────────────────────╲
        ↓
   ┌──────────┐
   │ count = 0│
   │  num = 1 │
   └────┬─────┘
        ↓
      ↑
     ╱ ╲
    ╱   ╲
   ╱     ╲
  ╱ num <= n?╲─────┐
 ╱   (判断)   ╲    │
     ╱   ╲       │
   是     否      │
   ╱       ╲     │
   ↓         ╲   │
n % num == 0?    ╲
   ╱   ╲        ╲
 是     否        ╲
  ╱        ╲      ╲
 ↓          ╲      ╲
┌─────────┐   ╲     ╲
│count=c+1│    ╲    ╲
└────┬────┘     ╲    ╲
     └───┬───────╲    ╲
         ↓         ╲   ╲
    ┌─────────┐    ╲  ╲
    │num=num+1│     ↓ ╲
    └────┬────┘    ┌───┘
         │
         └───────────┘
               ↓
   ╱────────────────────╲
  ╱ 输出: count         ╲
 ╱────────────────────────╲
        ↓
   ┌─────────┐
   │  结束   │
   └─────────┘
👉 点击查看答案

分析:

  • 这个程序计算:1到n中有多少个数能整除n
  • 也就是:n的因数个数
  • 例如:n = 6,因数有1, 2, 3, 6,共4个

代码:

# 计算n的因数个数
n = int(input("请输入n:"))

count = 0
num = 1

while num <= n:
    if n % num == 0:
        count = count + 1
    num = num + 1

print(f"{n}的因数个数是:{count}")

运行示例:

请输入n:6
6的因数个数是:4

解释:

  • 6的因数:1, 2, 3, 6
  • 6 % 1 == 0 → count = 1
  • 6 % 2 == 0 → count = 2
  • 6 % 3 == 0 → count = 3
  • 6 % 4 != 0 → count不变
  • 6 % 5 != 0 → count不变
  • 6 % 6 == 0 → count = 4

for循环的特殊流程图

for循环的流程图有一个需要注意的地方:循环变量的自动更新

示例:对比while和for的流程图

while版本:

i = 1
while i <= 5:
    print(i)
    i = i + 1  # 手动更新

for版本:

for i in range(1, 6):
    print(i)
    # 自动更新,不需要i = i + 1

对比流程图:

while流程图:

   ┌──────┐
   │ i=1  │
   └───┬──┘
       ↓
     ↑
    ╱ ╲
   ╱   ╲
  ╱i<=5?╲───┐
   ╱   ╲   │
  是    否  │
  ╱      ╲ │
 ↓        ╲│
┌────┐   ↓
│输出i│  ┌──────┐
└─┬──┘  │ i=i+1│ ← 手动更新
  │     └──┬───┘
  └───────┘

for流程图:

   ┌─────────────┐
   │ i = range   │
   │  (1, 6)     │
   └─────┬───────┘
         ↓
       ↑
      ╱ ╲
     ╱   ╲
    ╱i<=5?╲───┐
     ╱   ╲   │
    是    否  │
    ╱      ╲ │
   ↓        ╲│
 ┌───┐     ↓
 │输出i│   ┌─────────┐
 └─┬─┘   │i自动+1  │ ← 自动更新
   │     └────┬────┘
   │          │
   └──────────┘

给家长的小贴士:

  • for循环的更新是“隐式“的(自动完成)
  • 在流程图中,可以用虚线框表示自动操作
  • 或者用注释说明:“自动更新”
  • 强调:for循环更简洁,不容易忘记更新

流程图的实际应用

流程图不仅仅是学习工具,在实际编程中也非常有用。

应用1:设计程序

在写代码之前,先用流程图设计程序结构。

例子:设计一个登录验证程序

   ┌─────────┐
   │  开始   │
   └────┬────┘
        ↓
   ┌──────────────────┐
   │设置正确的用户名  │
   │和密码            │
   └────┬─────────────┘
        ↓
      ↑
     ╱ ╲
    ╱   ╲
   ╱     ╲
  ╱登录成功?╲────┐
 ╱   (判断)  ╲   │
     ╱   ╲     │
   否     是    │
   ╱       ╲   │
   ↓         ╲ │
╱──────────╲  ╲│
╱ 输入用户名 ╲  ↓
╱和密码      ╲┌─────┐
╱────────────╲│结束 │
   └────┬────┘└─────┘
        │
        └───────────┘

根据流程图写代码:

# 登录验证
correct_username = "admin"
correct_password = "123456"

while True:
    username = input("请输入用户名:")
    password = input("请输入密码:")

    if username == correct_username and password == correct_password:
        print("登录成功!")
        break
    else:
        print("用户名或密码错误,请重试!")

应用2:调试程序

当程序出现问题时,画出流程图帮助找出错误。

例子:调试一个有bug的程序

有bug的代码:

# 计算1到n的和(有bug)
n = 5
sum = 0

while i <= n:  # 错误!i没有定义
    sum = sum + i
    i = i + 1  # 错误!i第一次使用时不存在

print(sum)

画流程图找问题:

   ┌──────┐
   │ n=5  │
   └───┬──┘
       ↓
   ┌──────┐
   │sum=0 │
   └───┬──┘
       ↓
     ↑
    ╱ ╲
   ╱   ╲
  ╱i<=n?╲ ← 问题:i没有初始值!
   ╱   ╲
  是    否
  ╱      ╲
 ↓        ╲
...        ↓
         ┌─────┐
         │输出sum│
         └─────┘

发现问题:循环变量i没有初始值!

修正:

# 修正后的代码
n = 5
sum = 0
i = 1  # 添加初始值

while i <= n:
    sum = sum + i
    i = i + 1

print(sum)

应用3:与他人交流

流程图是程序员之间交流的好工具,能让别人快速理解你的程序逻辑。

给家长的小贴士:

  • 鼓励孩子在写复杂程序前先画流程图
  • 可以用纸笔,或者用专门的流程图软件
  • 流程图可以是“草图“,不需要太完美
  • 重点是理清思路,而不是画得漂亮

常见流程图符号的ASCII表示

在文本中画流程图时,我们常用ASCII字符来表示图形符号:

符号ASCII表示说明
开始/结束┌─────┐ (圆角矩形)或用(开始)表示
输入/输出╱ ╲ (平行四边形)倾斜的矩形
处理┌─────┐ (矩形)普通矩形
判断◇ 或 ╱ ╲ (菱形)有两个出口
流程线↓ ↑ │ →箭头表示方向

章节小结

我们学到了什么

  1. 流程图的基本符号:

    • 开始/结束:圆角矩形
    • 输入/输出:平行四边形
    • 处理:矩形
    • 判断:菱形
    • 流程线:箭头
  2. 用流程图表示程序结构:

    • 顺序语句:箭头一直向下,无分支
    • 条件语句:菱形判断,两个分支,无返回箭头
    • 循环语句:菱形判断,有返回箭头形成回路
  3. break和continue的流程图:

    • break:跳出循环,不再返回
    • continue:跳过本次,立即返回判断条件
  4. 流程图的应用:

    • 设计程序
    • 调试程序
    • 与他人交流

重要概念对比

结构流程图特征Python关键字
顺序箭头向下,无分支-
条件菱形判断,无返回if, else, elif
循环菱形判断,有返回while, for
break箭头跳出循环break
continue箭头返回判断continue

画流程图的步骤

  1. 从代码画流程图:

    • 找出程序的开始和结束
    • 识别输入和输出
    • 找出所有的判断点
    • 判断是条件还是循环(有无返回箭头)
    • 用箭头连接各部分
  2. 从流程图写代码:

    • 从开始到结束,依次转换符号
    • 输入/输出符号 → input()/print()
    • 处理符号 → 赋值语句
    • 菱形判断 → if语句或循环
    • 有返回箭头 → while/for循环
    • 无返回箭头 → if语句

编程技巧

  1. 先设计后编码:复杂程序先画流程图
  2. 分层理解:嵌套结构分内外层理解
  3. 用不同颜色:不同层级用不同颜色标记
  4. 简化测试:用小数字测试流程图逻辑
  5. 工具辅助:可以用专门的流程图软件

下一步

在下一章(第10章),我们将学习列表(List),这是一种新的数据结构,可以存储多个数据。结合循环和列表,我们可以处理更复杂的数据!

挑战练习

  1. 必做:

    • 画出“计算阶乘“程序的流程图(n! = 1×2×3×…×n)
    • 画出“判断素数“程序的流程图
    • 根据流程图写出“找最大值“的代码
  2. 选做:

    • 画出“九九乘法表“程序的流程图
    • 画出“斐波那契数列“程序的流程图
    • 根据流程图写出“存钱问题“的代码
  3. 挑战:

    • 设计一个流程图:用户输入5个数,找出最大值、最小值和平均值
    • 画出完整的“猜数字游戏“流程图(包括次数限制)
    • 用流程图设计一个简单的“学生成绩管理系统“
  4. 综合项目:

    • 选择你之前写过的最复杂的程序,画出它的流程图
    • 用流程图设计一个“文字冒险游戏“
    • 尝试使用流程图软件(如draw.io)绘制专业的流程图

加油!你已经学会了用图形方式表示程序的逻辑!🎉

流程图是程序员的“地图“,它能帮你和他人更好地理解程序的运行逻辑。继续努力,成为一名优秀的程序员!💪

数据结构:List(列表)

引言

在前面的章节中,我们学习了三种基本的数据类型:

  • 字符串(第3章):存储文字,比如“Hello“
  • 数值(第4章):存储数字,比如42、3.14
  • 布尔值(第5章):存储True或False

但是,如果我们想要存储多个数据呢?

比如:

  • 存储一个班级所有学生的名字
  • 存储一周7天的温度
  • 存储购物清单上的所有商品

这时候,我们就需要一种新的数据类型:列表(List)

列表(List)就像一个有顺序的容器,可以存放多个数据。它就像:

  • 一串珍珠项链(每颗珍珠都是一个数据)
  • 一列火车(每节车厢都是一个数据)
  • 一个多层笔筒(每层都放一支笔)

这一章,我们将学习:

  • 什么是列表
  • 列表在计算机内存中是如何存储的
  • 如何创建和使用列表
  • 列表的基本操作(索引、切片、长度)
  • 列表的常用方法(添加、删除、修改元素)
  • 如何遍历列表
  • 列表在数学中的应用(统计、排序)
  • 元组(Tuple)简介

给家长的小贴士:

  • 列表是Python中最常用的数据结构之一
  • 可以用生活中的例子类比:购物清单、点名册、书架上的书
  • 强调列表的“有序性“和“可变性“
  • 本章会引入内存的概念,帮助孩子理解数据在计算机中的存储方式
  • 鼓励孩子用列表解决实际问题(如记录朋友的名字)

列表在内存中的存储

在学习列表的操作之前,我们先来了解一个重要的问题:列表在计算机内存中是如何存储的?

什么是内存

还记得我们在第3章学过的“变量盒子“吗?每个变量都存储在计算机的内存(Memory)中。

内存就像一个巨大的储物柜,有很多个小格子(存储单元),每个格子都有一个编号,这个编号叫做地址

内存示意图:
┌────┬────┬────┬────┬────┬────┬────┬────┐
│    │    │    │    │    │    │    │    │ ...
│ 0  │ 1  │ 2  │ 3  │ 4  │ 5  │ 6  │ 7  │  (内存地址)
└────┴────┴────┴────┴────┴────┴────┴────┘

重要特点:

  • 每个格子都有唯一的地址编号
  • 地址从0开始,依次递增
  • 我们可以把数据存储在这些格子里

列表的连续存储

列表在内存中是连续存放的,这意味着列表的元素占据内存中相邻的格子。

示例:创建一个列表 numbers = [10, 20, 30, 40]

内存示意图:
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│     │     │     │     │     │     │     │     │
│ 10  │ 20  │ 30  │ 40  │ ... │     │     │     │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
  ↑           ↑           ↑
地址: 1000     1004       1008
索引:   0           1          2

列表的"索引"就是从开头数的第几个
(这里假设每个数字占4个格子)

要点:

  • 列表的元素在内存中挨着存放
  • 索引0对应第一个元素,索引1对应第二个元素…
  • 内存地址是连续的(比如1000, 1004, 1008…)

为什么列表要连续存储?

优点1:快速访问

  • 因为元素是连续存放的,计算机可以通过索引快速计算出元素的内存地址
  • 公式:元素地址 = 列表起始地址 + 索引 × 每个元素的大小
  • 这就是为什么list[2]能立刻找到第三个元素!

优点2:内存利用率高

  • 连续存储不会留下“空洞“
  • 就像在电影院里,大家挨着坐,不浪费座位

缺点:插入和删除较慢

  • 如果要在中间插入一个元素,需要把后面的元素都往后移动
  • 就像排队时,中间插一个人,后面的人都要往后挪一步

给家长的小贴士:

  • 这个概念比较抽象,可以用“电影院座位“类比
  • 连续存储就像大家一起看电影,座位挨着
  • 索引就像是“第几排第几座“
  • 小学阶段只需要理解“列表是连续存放的“这个概念即可
  • 不需要深入理解内存地址计算等细节

列表占用的内存

问题:一个列表占用多少内存?

答案:这取决于两个因素:

  1. 元素的个数(列表长度)
  2. 每个元素的大小(数据类型)

示例:

# 创建不同类型的列表
numbers = [1, 2, 3, 4, 5]        # 5个整数
names = ["小明", "小红", "小刚"]   # 3个字符串
mixed = [1, "你好", 3.14, True]   # 4个不同类型

内存占用估算(简化版):

  • 整数(int):大约占用28个字节
  • 小数(float):大约占用24个字节
  • 字符串(str):占用字节数 = 字符数 + 一些额外开销
import sys  # 导入sys模块,用于查看内存占用

# 查看一个列表占用的字节数
numbers = [1, 2, 3, 4, 5]
print(f"列表占用内存:{sys.getsizeof(numbers)}字节")  # 约120字节

# 查看一个整数占用的字节数
print(f"一个整数占用内存:{sys.getsizeof(1)}字节")   # 约28字节

小练习:估算列表 [10, 20, 30, 40, 50] 占用的内存

  • 5个整数 × 28字节 = 140字节
  • 再加上列表本身的额外开销(大约40字节)
  • 总共约180字节

给家长的小贴士:

  • sys.getsizeof()函数可以查看Python对象占用的内存
  • 这是一个很好的机会让孩子理解“数据占用空间“的概念
  • 可以让孩子比较不同列表的内存占用,理解“空间换时间“的权衡
  • 强调:不同的计算机系统可能略有差异

认识列表

什么是列表

列表是一个有序的数据集合,可以存储多个数据。列表中的每个数据称为元素(element)。

示例:

# 创建一个列表,存储几种水果
fruits = ["苹果", "香蕉", "橙子", "葡萄"]

print(fruits)  # 输出整个列表

运行结果:

['苹果', '香蕉', '橙子', '葡萄']

要点:

  • 列表用方括号[]表示
  • 元素之间用逗号,分隔
  • 列表可以存储任意类型的数据
  • 列表中的元素有顺序(从0开始编号)

创建列表的方法

方法1:直接创建列表

# 创建空列表
empty_list = []

# 创建有元素的列表
numbers = [1, 2, 3, 4, 5]
names = ["小明", "小红", "小刚"]
mixed = [1, "你好", 3.14, True]  # 混合类型

方法2:使用list()函数

# 创建空列表
empty_list = list()

# 从字符串创建列表(字符串也是特殊的列表!)
chars = list("Hello")  # ['H', 'e', 'l', 'l', 'o']

给家长的小贴士:

  • 空列表[]是创建列表的常用方式
  • 列表可以混合不同类型的数据(但实际编程中通常放同类型数据)
  • 可以用type()函数验证变量类型
  • 让孩子试试创建自己的列表(如喜欢的颜色、朋友的名字)

列表的特点

特点1:有序性

# 顺序不同,就是不同的列表
list1 = [1, 2, 3]
list2 = [3, 2, 1]

print(list1 == list2)  # False(顺序不同,不相等)

特点2:可变性

# 列表可以修改
fruits = ["苹果", "香蕉", "橙子"]
fruits[0] = "草莓"  # 修改第一个元素
print(fruits)  # ['草莓', '香蕉', '橙子']

特点3:可重复

# 列表可以有重复元素
numbers = [1, 2, 2, 3, 3, 3]
print(numbers)  # [1, 2, 2, 3, 3, 3]

练习1:创建你自己的列表

题目:创建一个列表,存储你最喜欢的5种食物。

👉 点击查看参考答案
# 创建食物列表
favorite_foods = ["披萨", "冰淇淋", "汉堡", "寿司", "巧克力"]

print("我最喜欢的食物:")
print(favorite_foods)

扩展:试试创建其他列表,如:

  • 喜欢的颜色
  • 想去的地方
  • 擅长的运动

访问列表元素

索引(Index)

列表中的每个元素都有一个编号,叫做索引(index)。索引从0开始,不是从1开始!

索引示意图:

列表:  ["苹果", "香蕉", "橙子", "葡萄"]
索引:    0      1      2      3

反向索引: -4     -3     -2     -1

内存地址示意:

内存:   [地址1] [地址2] [地址3] [地址4]
        ["苹果"] ["香蕉"] ["橙子"] ["葡萄"]
索引:      0       1       2       3

示例1:正向索引(从0开始)

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

print(fruits[0])  # 苹果(第一个元素)
print(fruits[1])  # 香蕉(第二个元素)
print(fruits[2])  # 橙子(第三个元素)
print(fruits[3])  # 葡萄(第四个元素)

示例2:反向索引(从-1开始)

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

print(fruits[-1])  # 葡萄(倒数第一个)
print(fruits[-2])  # 橙子(倒数第二个)
print(fruits[-3])  # 香蕉(倒数第三个)
print(fruits[-4])  # 苹果(倒数第四个)

给家长的小贴士:

  • 重要:Python的索引从0开始,这是初学者最容易犯错的地方!
  • 用“楼层数“类比:地面是0层,1楼是第1层
  • 反向索引从-1开始:-1是最后一个,-2是倒数第2个
  • 可以画图帮助孩子理解索引和位置的关系
  • 强调:索引是相对于列表开头的“偏移量“

索引与内存地址的关系

深入理解:索引实际上就是相对于列表起始位置的“偏移量“

假设列表从内存地址1000开始:
每个元素占用8个字节(假设)

索引0:地址 = 1000 + 0×8 = 1000
索引1:地址 = 1000 + 1×8 = 1008
索引2:地址 = 1000 + 2×8 = 1016
索引3:地址 = 1000 + 3×8 = 1024

这就是为什么通过索引可以快速找到元素!

代码示例:

# 查看列表的内存地址
import sys

numbers = [10, 20, 30, 40]

# 打印列表的内存地址(十六进制)
print(f"列表的内存地址:{id(numbers)}")

# 打印每个元素的索引和值
for i in range(len(numbers)):
    print(f"索引{i}:值{numbers[i]}")

给家长的小贴士:

  • 这个概念比较深,小学阶段只需要简单了解
  • 核心思想:索引是用来“计算“内存位置的
  • 这解释了为什么列表访问很快(O(1)时间复杂度)
  • 不需要孩子掌握内存地址计算的细节

索引越界(IndexError)

如果访问一个不存在的索引,Python会报错:IndexError(索引错误)。

错误示例:

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

# 这个列表有4个元素,索引是0, 1, 2, 3
print(fruits[4])  # 错误!索引4不存在!

错误信息:

IndexError: list index out of range

解释:

  • 列表有4个元素,索引范围是0到3
  • 访问索引4会越界
  • 就像你想住5号房间,但宾馆只有4层楼

避免索引越界的方法:

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

# 方法1:使用len()检查索引范围
if len(fruits) > 4:
    print(fruits[4])
else:
    print("索引4不存在")

# 方法2:使用反向索引(更安全)
print(fruits[-1])  # 最后一个元素

实践1:访问列表元素

题目:给定一个列表,完成以下任务。

# 学生成绩列表
scores = [85, 92, 78, 95, 88]

任务:

  1. 打印第一个成绩
  2. 打印最后一个成绩
  3. 打印中间的成绩(第3个)
  4. 打印倒数第二个成绩
👉 点击查看答案
# 学生成绩列表
scores = [85, 92, 78, 95, 88]

# 1. 第一个成绩
print(f"第一个成绩:{scores[0]}")  # 85

# 2. 最后一个成绩
print(f"最后一个成绩:{scores[-1]}")  # 88

# 3. 中间的成绩(第3个)
print(f"第3个成绩:{scores[2]}")  # 78

# 4. 倒数第二个成绩
print(f"倒数第二个成绩:{scores[-2]}")  # 95

运行结果:

第一个成绩:85
最后一个成绩:88
第3个成绩:78
倒数第二个成绩:95

练习2:索引练习

题目:下面的程序会输出什么?

colors = ["红", "橙", "黄", "绿", "青", "蓝", "紫"]

print(colors[2])
print(colors[-3])
print(colors[0])
print(colors[-1])
👉 点击查看答案

输出:

黄
绿
红
紫

解释:

  • colors[2]:索引2是第3个元素“黄“
  • colors[-3]:倒数第3个元素“绿“
  • colors[0]:第1个元素“红“
  • colors[-1]:最后1个元素“紫“

切片(Slice)

切片可以获取列表的一部分,创建一个新的列表。

切片语法:list[start:end:step]

  • start:起始索引(包含)
  • end:结束索引(不包含!)
  • step:步长(可选,默认为1)

基本切片

示例1:获取前几个元素

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 获取前3个元素(索引0, 1, 2)
print(numbers[0:3])  # [0, 1, 2]

# 简写:省略起始索引0
print(numbers[:3])  # [0, 1, 2]

示例2:获取中间的元素

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 获取索引2到6的元素(不包含6)
print(numbers[2:6])  # [2, 3, 4, 5]

示例3:获取后几个元素

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 获取最后3个元素
print(numbers[-3:])  # [7, 8, 9]

# 获取从索引7到最后的元素
print(numbers[7:])  # [7, 8, 9]

示例4:获取整个列表

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 方法1:省略start和end
print(numbers[:])  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 方法2:使用完整范围
print(numbers[0:len(numbers)])  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

带步长的切片

示例1:每隔几个元素取一个

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 每隔2个取一个(取索引0, 2, 4, 6, 8)
print(numbers[::2])  # [0, 2, 4, 6, 8]

# 每隔3个取一个
print(numbers[::3])  # [0, 3, 6, 9]

示例2:反向切片

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 反向列表(步长为-1)
print(numbers[::-1])  # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

给家长的小贴士:

  • 切片的核心是**“包含起始,不包含结束”**
  • 用“切蛋糕“类比:从哪里切,到哪里切
  • 省略起始表示从头开始,省略结束表示到末尾
  • 负数步长会反向取元素
  • 让孩子用可视化工具(如切片可视化网站)帮助理解

切片图解

列表:  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
索引:   0  1  2  3  4  5  6  7  8  9

切片 [2:6]:
       [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
              ↑        ↑
            start     end(不包含)
结果:          [2, 3, 4, 5]

练习3:切片练习

题目:预测下面的切片结果。

letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']

# 预测结果
print(letters[2:5])
print(letters[:4])
print(letters[5:])
print(letters[::2])
print(letters[::-1])
👉 点击查看答案

结果:

['C', 'D', 'E']
['A', 'B', 'C', 'D']
['F', 'G', 'H']
['A', 'C', 'E', 'G']
['H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']

解释:

  • letters[2:5]:索引2到4,即[‘C’, ‘D’, ‘E’]
  • letters[:4]:开头到索引3,即[‘A’, ‘B’, ‘C’, ‘D’]
  • letters[5:]:索引5到末尾,即[‘F’, ‘G’, ‘H’]
  • letters[::2]:每隔一个取一个,即[‘A’, ‘C’, ‘E’, ‘G’]
  • letters[::-1]:反向,即[‘H’, ‘G’, ‘F’, ‘E’, ‘D’, ‘C’, ‘B’, ‘A’]

列表的长度

使用len()函数可以获取列表中元素的个数。

示例:

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

# 获取长度
print(len(fruits))  # 4

# 空列表的长度
empty_list = []
print(len(empty_list))  # 0

为什么长度很重要

场景1:避免索引越界

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

# 错误:直接访问可能不存在的索引
# print(fruits[10])  # IndexError!

# 正确:先检查长度
if len(fruits) > 10:
    print(fruits[10])
else:
    print("索引10不存在")

场景2:循环访问所有元素

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

# 使用range和len循环
for i in range(len(fruits)):
    print(f"索引{i}:{fruits[i]}")

输出:

索引0:苹果
索引1:香蕉
索引2:橙子
索引3:葡萄

实践2:使用长度

题目:打印列表的所有元素,格式为“第i个元素是:xxx“。

animals = ["猫", "狗", "兔子", "仓鼠"]
👉 点击查看答案
animals = ["猫", "狗", "兔子", "仓鼠"]

# 使用range和len
for i in range(len(animals)):
    print(f"第{i + 1}个元素是:{animals[i]}")

输出:

第1个元素是:猫
第2个元素是:狗
第3个元素是:兔子
第4个元素是:仓鼠

注意:我们用i + 1是因为索引从0开始,但我们要显示“第1个“、“第2个“等(从1开始)。

修改列表元素

列表是可变的(mutable),这意味着我们可以修改、添加或删除元素。

修改单个元素

语法:list[index] = new_value

示例:

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

# 修改第1个元素(索引0)
fruits[0] = "草莓"
print(fruits)  # ['草莓', '香蕉', '橙子', '葡萄']

# 修改最后一个元素
fruits[-1] = "西瓜"
print(fruits)  # ['草莓', '香蕉', '橙子', '西瓜']

修改多个元素(使用切片)

示例1:用切片修改

numbers = [0, 1, 2, 3, 4, 5]

# 修改索引2到4的元素
numbers[2:5] = [20, 30, 40]
print(numbers)  # [0, 1, 20, 30, 40, 5]

示例2:替换不同数量的元素

numbers = [0, 1, 2, 3, 4, 5]

# 用2个元素替换3个元素
numbers[1:4] = [10, 20]
print(numbers)  # [0, 10, 20, 4, 5]

# 用4个元素替换2个元素
numbers[1:3] = [100, 200, 300, 400]
print(numbers)  # [0, 100, 200, 300, 400, 4, 5]

练习4:修改列表

题目:完成以下任务。

scores = [85, 92, 78, 95, 88]
  1. 将第一个成绩修改为90
  2. 将最后一个成绩修改为100
  3. 将中间三个成绩修改为80, 85, 90
👉 点击查看答案
scores = [85, 92, 78, 95, 88]

# 1. 修改第一个成绩
scores[0] = 90
print(f"第一次修改后:{scores}")  # [90, 92, 78, 95, 88]

# 2. 修改最后一个成绩
scores[-1] = 100
print(f"第二次修改后:{scores}")  # [90, 92, 78, 95, 100]

# 3. 修改中间三个成绩
scores[1:4] = [80, 85, 90]
print(f"第三次修改后:{scores}")  # [90, 80, 85, 90, 100]

列表的常用方法

Python提供了很多列表方法(list methods),帮助我们方便地操作列表。

添加元素

append():在末尾添加元素

语法:list.append(item)

示例:

fruits = ["苹果", "香蕉"]

# 在末尾添加一个元素
fruits.append("橙子")
print(fruits)  # ['苹果', '香蕉', '橙子']

fruits.append("葡萄")
print(fruits)  # ['苹果', '香蕉', '橙子', '葡萄']

insert():在指定位置插入元素

语法:list.insert(index, item)

示例:

fruits = ["苹果", "香蕉", "葡萄"]

# 在索引1的位置插入"橙子"
fruits.insert(1, "橙子")
print(fruits)  # ['苹果', '橙子', '香蕉', '葡萄']

# 在开头插入
fruits.insert(0, "草莓")
print(fruits)  # ['草莓', '苹果', '橙子', '香蕉', '葡萄']

extend():合并两个列表

语法:list.extend(other_list)

示例:

fruits = ["苹果", "香蕉"]
more_fruits = ["橙子", "葡萄", "西瓜"]

# 将more_fruits的所有元素添加到fruits
fruits.extend(more_fruits)
print(fruits)  # ['苹果', '香蕉', '橙子', '葡萄', '西瓜']

对比append和extend:

list1 = [1, 2]
list2 = [3, 4]

# append:将list2作为一个元素添加
list1.append(list2)
print(list1)  # [1, 2, [3, 4]]

# extend:将list2的元素展开添加
list1 = [1, 2]
list1.extend(list2)
print(list1)  # [1, 2, 3, 4]

删除元素

del:根据索引删除

语法:del list[index]

示例:

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

# 删除索引1的元素
del fruits[1]
print(fruits)  # ['苹果', '橙子', '葡萄']

# 删除第一个元素
del fruits[0]
print(fruits)  # ['橙子', '葡萄']

remove():根据值删除

语法:list.remove(value)

示例:

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

# 删除值为"香蕉"的元素
fruits.remove("香蕉")
print(fruits)  # ['苹果', '橙子', '葡萄']

注意:如果值不存在,会报错ValueError

安全的删除方法:

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

# 先检查是否存在
if "西瓜" in fruits:
    fruits.remove("西瓜")
else:
    print("西瓜不在列表中")

pop():删除并返回元素

语法:list.pop(index)(默认删除最后一个)

示例:

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

# 删除最后一个元素
last_fruit = fruits.pop()
print(last_fruit)  # 葡萄
print(fruits)  # ['苹果', '香蕉', '橙子']

# 删除指定索引的元素
second_fruit = fruits.pop(1)
print(second_fruit)  # 香蕉
print(fruits)  # ['苹果', '橙子']

排序

sort():原地排序

语法:list.sort()(默认升序)

示例:

numbers = [3, 1, 4, 1, 5, 9, 2, 6]

# 升序排序
numbers.sort()
print(numbers)  # [1, 1, 2, 3, 4, 5, 6, 9]

# 降序排序
numbers.sort(reverse=True)
print(numbers)  # [9, 6, 5, 4, 3, 2, 1, 1]

字符串排序:

fruits = ["香蕉", "苹果", "橙子", "葡萄"]

# 按字母顺序排序
fruits.sort()
print(fruits)  # ['葡萄', '橙子', '苹果', '香蕉']

sorted():返回新列表

语法:sorted(list)

示例:

numbers = [3, 1, 4, 1, 5, 9, 2, 6]

# sorted返回新列表,原列表不变
sorted_numbers = sorted(numbers)
print(sorted_numbers)  # [1, 1, 2, 3, 4, 5, 6, 9]
print(numbers)  # [3, 1, 4, 1, 5, 9, 2, 6](原列表不变)

反转

reverse():原地反转

numbers = [1, 2, 3, 4, 5]

numbers.reverse()
print(numbers)  # [5, 4, 3, 2, 1]

其他常用方法

方法作用示例
len(list)返回列表长度len([1, 2, 3]) → 3
list.count(item)统计元素出现次数[1, 2, 2, 3].count(2) → 2
list.index(item)返回元素的索引['a', 'b', 'c'].index('b') → 1
list.clear()清空列表[1, 2, 3].clear()[]
item in list判断元素是否存在'a' in ['a', 'b'] → True
item not in list判断元素是否不存在'c' not in ['a', 'b'] → True

给家长的小贴士:

  • 列表方法分为“原地修改“和“返回新列表“两种
  • append、insert、extend、sort、reverse是原地修改
  • sorted返回新列表,原列表不变
  • 强调innot in的用法,非常实用
  • 可以制作方法卡片,让孩子随时查阅

实践3:列表方法综合练习

题目:完成以下任务。

numbers = [3, 1, 4, 1, 5, 9, 2, 6]
  1. 在末尾添加数字8
  2. 在开头添加数字0
  3. 删除值为1的第一个元素
  4. 统计数字1出现的次数
  5. 判断数字7是否在列表中
  6. 对列表进行升序排序
👉 点击查看答案
numbers = [3, 1, 4, 1, 5, 9, 2, 6]

# 1. 在末尾添加数字8
numbers.append(8)
print(f"添加8后:{numbers}")  # [3, 1, 4, 1, 5, 9, 2, 6, 8]

# 2. 在开头添加数字0
numbers.insert(0, 0)
print(f"添加0后:{numbers}")  # [0, 3, 1, 4, 1, 5, 9, 2, 6, 8]

# 3. 删除值为1的第一个元素
numbers.remove(1)
print(f"删除1后:{numbers}")  # [0, 3, 4, 1, 5, 9, 2, 6, 8]

# 4. 统计数字1出现的次数
count = numbers.count(1)
print(f"数字1出现{count}次")  # 数字1出现1次

# 5. 判断数字7是否在列表中
if 7 in numbers:
    print("数字7在列表中")
else:
    print("数字7不在列表中")

# 6. 对列表进行升序排序
numbers.sort()
print(f"排序后:{numbers}")  # [0, 1, 2, 3, 4, 5, 6, 8, 9]

遍历列表

遍历(Traverse)就是依次访问列表中的每个元素。我们有两种主要方式来遍历列表。

方式1:遍历元素(不需要索引)

语法:

for item in list:
    # 处理item

示例:

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

# 直接遍历元素
for fruit in fruits:
    print(f"我喜欢吃{fruit}")

输出:

我喜欢吃苹果
我喜欢吃香蕉
我喜欢吃橙子
我喜欢吃葡萄

适用场景:

  • 只需要访问元素,不需要知道索引
  • 对每个元素执行相同的操作

方式2:遍历索引(需要索引)

语法:

for i in range(len(list)):
    # 通过list[i]访问元素

示例:

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

# 遍历索引
for i in range(len(fruits)):
    print(f"索引{i}:{fruits[i]}")

输出:

索引0:苹果
索引1:香蕉
索引2:橙子
索引3:葡萄

适用场景:

  • 需要修改列表元素
  • 需要知道元素的索引位置
  • 需要同时访问多个列表

方式3:同时获取索引和元素(enumerate)

语法:

for index, item in enumerate(list):
    # index是索引,item是元素

示例:

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

# 使用enumerate同时获取索引和元素
for index, fruit in enumerate(fruits):
    print(f"索引{index}:{fruit}")

输出:

索引0:苹果
索引1:香蕉
索引2:橙子
索引3:葡萄

enumerate的起始索引参数:

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

# 从1开始编号
for index, fruit in enumerate(fruits, start=1):
    print(f"第{index}个:{fruit}")

输出:

第1个:苹果
第2个:香蕉
第3个:橙子
第4个:葡萄

两种遍历方式的对比

场景1:只需要访问元素

# 推荐:遍历元素
for fruit in fruits:
    print(fruit)

场景2:需要修改元素

# 推荐:遍历索引
for i in range(len(scores)):
    scores[i] = scores[i] + 5  # 每个成绩加5分

场景3:需要索引和元素

# 推荐:使用enumerate
for i, fruit in enumerate(fruits):
    print(f"索引{i}:{fruit}")

给家长的小贴士:

  • 重要:遍历时如果需要修改元素,必须用索引方式
  • 遍历元素更简洁,但无法修改元素
  • enumerate是Python特有的,非常实用
  • 可以让孩子对比三种方式,理解它们的区别
  • 强调“从需求出发选择遍历方式“

练习5:遍历列表

题目1:计算列表中所有成绩的平均值。

scores = [85, 92, 78, 95, 88]
👉 点击查看答案
scores = [85, 92, 78, 95, 88]

# 方法1:遍历元素
total = 0
for score in scores:
    total = total + score

average = total / len(scores)
print(f"平均成绩:{average}")  # 平均成绩:87.6

# 方法2:使用sum函数(更简洁)
average = sum(scores) / len(scores)
print(f"平均成绩:{average}")  # 平均成绩:87.6

题目2:将列表中所有偶数乘以2。

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
👉 点击查看答案
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 需要修改元素,使用索引遍历
for i in range(len(numbers)):
    if numbers[i] % 2 == 0:  # 如果是偶数
        numbers[i] = numbers[i] * 2

print(numbers)  # [1, 4, 3, 8, 5, 12, 7, 16, 9, 20]

题目3:打印列表及其索引(从1开始)。

colors = ["红", "橙", "黄", "绿", "蓝"]
👉 点击查看答案
colors = ["红", "橙", "黄", "绿", "蓝"]

# 使用enumerate,从1开始
for i, color in enumerate(colors, start=1):
    print(f"第{i}种颜色:{color}")

输出:

第1种颜色:红
第2种颜色:橙
第3种颜色:黄
第4种颜色:绿
第5种颜色:蓝

数学应用:统计数据

列表在数学中有很多应用,特别是统计相关的问题。让我们用列表来解决一些数学问题!

基本统计量

在数学课上,你学过以下统计量:

  • 平均数(Mean):所有数的和除以个数
  • 中位数(Median):排序后中间的数
  • 众数(Mode):出现次数最多的数
  • 最大值(Max):最大的数
  • 最小值(Min):最小的数
  • 极差(Range):最大值减最小值

应用1:计算平均数

数学定义: $$ \text{平均数} = \frac{\text{所有数的总和}}{\text{数的个数}} $$

Python实现:

# 方法1:手动计算
scores = [85, 92, 78, 95, 88]

total = 0
for score in scores:
    total = total + score

average = total / len(scores)
print(f"平均数:{average}")  # 平均数:87.6

# 方法2:使用sum函数
average = sum(scores) / len(scores)
print(f"平均数:{average}")  # 平均数:87.6

应用2:找出最大值和最小值

Python实现:

scores = [85, 92, 78, 95, 88]

# 使用内置函数
max_score = max(scores)
min_score = min(scores)

print(f"最大值:{max_score}")  # 最大值:95
print(f"最小值:{min_score}")  # 最小值:78

# 计算极差
range_ = max_score - min_score
print(f"极差:{range_}")  # 极差:17

应用3:计算中位数

数学定义:

  • 如果有奇数个数,中位数就是中间的数
  • 如果有偶数个数,中位数就是中间两个数的平均数

示例:

# 奇数个元素
numbers1 = [1, 3, 5, 7, 9]
# 中位数是5(第3个数)

# 偶数个元素
numbers2 = [1, 3, 5, 7, 9, 11]
# 中位数是(5+7)/2=6(第3和第4个数的平均)

Python实现:

def calculate_median(numbers):
    # 先排序
    sorted_numbers = sorted(numbers)
    n = len(sorted_numbers)

    # 奇数个元素
    if n % 2 == 1:
        median = sorted_numbers[n // 2]
    # 偶数个元素
    else:
        middle1 = sorted_numbers[n // 2 - 1]
        middle2 = sorted_numbers[n // 2]
        median = (middle1 + middle2) / 2

    return median

# 测试奇数个
numbers1 = [1, 3, 5, 7, 9]
print(f"中位数:{calculate_median(numbers1)}")  # 中位数:5

# 测试偶数个
numbers2 = [1, 3, 5, 7, 9, 11]
print(f"中位数:{calculate_median(numbers2)}")  # 中位数:6.0

应用4:找众数

数学定义:众数是出现次数最多的数。

Python实现:

def find_mode(numbers):
    # 统计每个数出现的次数
    count_dict = {}
    for num in numbers:
        if num in count_dict:
            count_dict[num] = count_dict[num] + 1
        else:
            count_dict[num] = 1

    # 找出出现次数最多的数
    max_count = 0
    mode = numbers[0]
    for num, count in count_dict.items():
        if count > max_count:
            max_count = count
            mode = num

    return mode

# 测试
numbers = [1, 2, 2, 3, 3, 3, 4]
print(f"众数:{find_mode(numbers)}")  # 众数:3

给家长的小贴士:

  • 这是很好的机会,用编程巩固数学知识
  • 可以让孩子手动计算,再用程序验证
  • 强调:数学公式和编程实现的对应关系
  • 如果孩子还没学过中位数和众数,可以先跳过
  • 鼓励孩子用列表解决实际问题(如统计考试成绩)

实践4:数学统计练习

题目:给定一组成绩,完成以下统计。

scores = [85, 92, 78, 95, 88, 65, 72, 58, 90, 82]

任务:

  1. 计算平均分
  2. 找出最高分和最低分
  3. 计算极差
  4. 统计及格人数(≥60分)
  5. 将成绩从低到高排序
👉 点击查看答案
scores = [85, 92, 78, 95, 88, 65, 72, 58, 90, 82]

# 1. 计算平均分
average = sum(scores) / len(scores)
print(f"平均分:{average:.1f}")  # 平均分:80.5

# 2. 找出最高分和最低分
max_score = max(scores)
min_score = min(scores)
print(f"最高分:{max_score}")  # 最高分:95
print(f"最低分:{min_score}")  # 最低分:58

# 3. 计算极差
range_ = max_score - min_score
print(f"极差:{range_}")  # 极差:37

# 4. 统计及格人数
pass_count = 0
for score in scores:
    if score >= 60:
        pass_count = pass_count + 1

print(f"及格人数:{pass_count}")  # 及格人数:9

# 5. 排序
sorted_scores = sorted(scores)
print(f"排序后:{sorted_scores}")
# [58, 65, 72, 78, 82, 85, 88, 90, 92, 95]

数学应用:数列

列表非常适合表示数列。在数学中,数列是按照一定规律排列的一列数。

等差数列

数学定义:相邻两项的差相等(公差d)。

示例:

2, 5, 8, 11, 14, ...
公差是3(每个数比前一个数大3)

Python生成等差数列:

# 生成前n项等差数列
def arithmetic_sequence(a1, d, n):
    """生成首项为a1,公差为d,共n项的等差数列"""
    sequence = []
    for i in range(n):
        term = a1 + i * d
        sequence.append(term)
    return sequence

# 示例:首项2,公差3,共10项
seq = arithmetic_sequence(2, 3, 10)
print(seq)
# [2, 5, 8, 11, 14, 17, 20, 23, 26, 29]

等差数列求和:

def sum_arithmetic_sequence(a1, d, n):
    """计算等差数列前n项和"""
    # 公式:和 = (首项 + 末项) × 项数 / 2
    an = a1 + (n - 1) * d  # 末项
    total = (a1 + an) * n // 2
    return total

# 示例:1+2+3+...+100
total = sum_arithmetic_sequence(1, 1, 100)
print(f"1+2+3+...+100={total}")  # 1+2+3+...+100=5050

等比数列

数学定义:相邻两项的比相等(公比q)。

示例:

2, 4, 8, 16, 32, ...
公比是2(每个数是前一个数的2倍)

Python生成等比数列:

def geometric_sequence(a1, q, n):
    """生成首项为a1,公比为q,共n项的等比数列"""
    sequence = []
    term = a1
    for i in range(n):
        sequence.append(term)
        term = term * q
    return sequence

# 示例:首项2,公比2,共10项
seq = geometric_sequence(2, 2, 10)
print(seq)
# [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]

斐波那契数列

数学定义:

  • 第1项是1
  • 第2项是1
  • 从第3项开始,每项等于前两项之和
1, 1, 2, 3, 5, 8, 13, 21, ...

Python生成斐波那契数列:

def fibonacci(n):
    """生成前n项斐波那契数列"""
    if n <= 0:
        return []
    elif n == 1:
        return [1]
    elif n == 2:
        return [1, 1]

    sequence = [1, 1]
    for i in range(2, n):
        next_term = sequence[-1] + sequence[-2]
        sequence.append(next_term)

    return sequence

# 示例:前15项
fib = fibonacci(15)
print(fib)
# [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

练习6:数列练习

题目1:生成从1到100的所有偶数,并计算它们的和。

👉 点击查看答案
# 方法1:使用range
evens = list(range(2, 101, 2))  # 从2开始,每次加2,到100
print(f"偶数列表:{evens}")

total = sum(evens)
print(f"偶数和:{total}")  # 偶数和:2550

# 方法2:使用循环
evens = []
for i in range(1, 51):
    evens.append(2 * i)

print(f"偶数列表:{evens}")
print(f"偶数和:{sum(evens)}")  # 偶数和:2550

题目2:生成2的n次方数列(1, 2, 4, 8, 16, …),共10项。

👉 点击查看答案
# 生成2的n次方数列
powers = []
for i in range(10):
    powers.append(2 ** i)

print(f"2的n次方数列:{powers}")
# [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]

# 计算总和
total = sum(powers)
print(f"总和:{total}")  # 总和:1023

题目3:找出100以内所有能被3整除的数,并统计个数。

👉 点击查看答案
# 方法1:使用range和list
multiples_of_3 = list(range(3, 101, 3))
print(f"能被3整除的数:{multiples_of_3}")
print(f"共{len(multiples_of_3)}个")  # 共33个

# 方法2:使用循环
multiples_of_3 = []
for i in range(1, 101):
    if i % 3 == 0:
        multiples_of_3.append(i)

print(f"能被3整除的数:{multiples_of_3}")
print(f"共{len(multiples_of_3)}个")  # 共33个

列表的综合应用

应用1:统计成绩分布

题目:给定一组成绩,统计不同等级的人数。

scores = [85, 92, 78, 95, 88, 65, 72, 58, 90, 82]

等级标准:

  • 优秀:≥90分
  • 良好:80-89分
  • 及格:60-79分
  • 不及格:<60分
👉 点击查看答案
scores = [85, 92, 78, 95, 88, 65, 72, 58, 90, 82]

# 统计各等级人数
excellent = 0  # 优秀(>=90)
good = 0       # 良好(80-89)
pass_ = 0      # 及格(60-79)
fail = 0       # 不及格(<60)

for score in scores:
    if score >= 90:
        excellent = excellent + 1
    elif score >= 80:
        good = good + 1
    elif score >= 60:
        pass_ = pass_ + 1
    else:
        fail = fail + 1

print("===== 成绩分布 =====")
print(f"优秀(≥90):{excellent}人")  # 优秀:3人
print(f"良好(80-89):{good}人")     # 良好:4人
print(f"及格(60-79):{pass_}人")     # 及格:2人
print(f"不及格(<60):{fail}人")      # 不及格:1人

运行结果:

===== 成绩分布 =====
优秀(≥90):3人
良好(80-89):4人
及格(60-79):2人
不及格(<60):1人

应用2:找最大值及其索引

题目:找出列表中的最大值及其索引位置。

numbers = [3, 1, 4, 1, 5, 9, 2, 6]
👉 点击查看答案
numbers = [3, 1, 4, 1, 5, 9, 2, 6]

# 方法1:使用max和index
max_value = max(numbers)
max_index = numbers.index(max_value)
print(f"最大值:{max_value},索引:{max_index}")
# 输出:最大值:9,索引:5

# 方法2:手动查找(理解算法)
max_value = numbers[0]
max_index = 0

for i in range(1, len(numbers)):
    if numbers[i] > max_value:
        max_value = numbers[i]
        max_index = i

print(f"最大值:{max_value},索引:{max_index}")
# 输出:最大值:9,索引:5

应用3:列表倒序

题目:将列表倒序,不使用reverse方法。

numbers = [1, 2, 3, 4, 5]
👉 点击查看答案
numbers = [1, 2, 3, 4, 5]

# 方法1:创建新列表
reversed_numbers = []
for i in range(len(numbers) - 1, -1, -1):  # 从最后一个到第一个
    reversed_numbers.append(numbers[i])

print(reversed_numbers)  # [5, 4, 3, 2, 1]

# 方法2:交换位置(原地修改)
for i in range(len(numbers) // 2):
    # 交换第i个和倒数第i个
    temp = numbers[i]
    numbers[i] = numbers[len(numbers) - 1 - i]
    numbers[len(numbers) - 1 - i] = temp

print(numbers)  # [5, 4, 3, 2, 1]

# 方法3:使用切片(最简洁)
print(numbers[::-1])  # [5, 4, 3, 2, 1]

字符串也是特殊的列表

在第3章我们学习了字符串,其实字符串也是一种特殊的列表!

字符串的列表特性

相似之处:

# 字符串可以像列表一样使用索引和切片
text = "Hello"

# 索引
print(text[0])  # H
print(text[-1])  # o

# 切片
print(text[1:4])  # ell
print(text[::-1])  # olleH(反向)

# 长度
print(len(text))  # 5

# 遍历
for char in text:
    print(char)

不同之处:

# 字符串是不可变的(immutable)
text = "Hello"

# 错误!不能修改字符串
# text[0] = "h"  # TypeError!

# 列表是可变的
numbers = [1, 2, 3]
numbers[0] = 10  # 可以修改
print(numbers)  # [10, 2, 3]

字符串和列表的转换

字符串 → 列表(list)

# 将字符串转换为字符列表
text = "Hello"
chars = list(text)
print(chars)  # ['H', 'e', 'l', 'l', 'o']

# 分割字符串为单词列表
sentence = "I love Python"
words = sentence.split()  # 按空格分割
print(words)  # ['I', 'love', 'Python']

# 按指定字符分割
date = "2024-02-14"
parts = date.split("-")
print(parts)  # ['2024', '02', '14']

列表 → 字符串(join)

# 将字符列表连接为字符串
chars = ['H', 'e', 'l', 'l', 'o']
text = "".join(chars)
print(text)  # Hello

# 用指定字符连接
words = ["I", "love", "Python"]
sentence = " ".join(words)
print(sentence)  # I love Python

# 用其他字符连接
parts = ["2024", "02", "14"]
date = "-".join(parts)
print(date)  # 2024-02-14

给家长的小贴士:

  • 强调字符串和列表的相似性,帮助孩子理解
  • 用“珍珠项链“类比:字符串是焊死的,列表是可拆卸的
  • split和join是非常实用的方法,重点讲解
  • 可以让孩子用split和join处理文本(如句子、日期)

实践5:字符串和列表转换

题目1:统计一个句子中有多少个单词。

sentence = "I love Python programming"
👉 点击查看答案
sentence = "I love Python programming"

# 方法1:使用split
words = sentence.split()
word_count = len(words)
print(f"单词数量:{word_count}")  # 单词数量:4

# 方法2:手动统计(计算空格+1)
space_count = 0
for char in sentence:
    if char == " ":
        space_count = space_count + 1

word_count = space_count + 1
print(f"单词数量:{word_count}")  # 单词数量:4

题目2:反转一个字符串。

text = "Hello"
👉 点击查看答案
text = "Hello"

# 方法1:使用切片
reversed_text = text[::-1]
print(reversed_text)  # olleH

# 方法2:转换为列表,反转,再连接
chars = list(text)
chars.reverse()
reversed_text = "".join(chars)
print(reversed_text)  # olleH

# 方法3:手动构建
reversed_text = ""
for i in range(len(text) - 1, -1, -1):
    reversed_text = reversed_text + text[i]

print(reversed_text)  # olleH

元组(Tuple)简介

元组(Tuple)是另一种有序的数据结构,它和列表非常相似,但有一个关键区别:元组是不可变的(immutable)。

元组的创建

语法:使用圆括号()代替方括号[]

# 创建元组
empty_tuple = ()  # 空元组
numbers = (1, 2, 3, 4, 5)
fruits = ("苹果", "香蕉", "橙子")

# 单个元素的元组(注意逗号!)
single = (1,)  # 必须有逗号
print(type(single))  # <class 'tuple'>

# 如果没有逗号,就不是元组
not_tuple = (1)
print(type(not_tuple))  # <class 'int'>

元组和列表的对比

特性列表(List)元组(Tuple)
符号[]()
可变性可变不可变
性能较慢较快
用途存储需要修改的数据存储固定数据
方法丰富较少

示例:

# 列表:可以修改
list1 = [1, 2, 3]
list1[0] = 10
print(list1)  # [10, 2, 3]

# 元组:不能修改
tuple1 = (1, 2, 3)
# tuple1[0] = 10  # TypeError!'tuple' object does not support item assignment

元组的常见用途

用途1:返回多个值

# 函数返回多个值(实际上是返回元组)
def get_user_info():
    name = "小明"
    age = 10
    grade = 5
    return name, age, grade  # 返回元组

user_info = get_user_info()
print(user_info)  # ('小明', 10, 5)

# 解包
name, age, grade = get_user_info()
print(f"姓名:{name},年龄:{age},年级:{grade}")

用途2:保护数据不被修改

# 常量配置
CONFIG = ("localhost", 8080, "admin")
# CONFIG[0] = "192.168.1.1"  # 错误!不能修改

用途3:字典的键

# 元组可以作为字典的键(后续章节学习)
coordinates = {(10, 20): "点A", (30, 40): "点B"}
# 列表不能作为字典的键

给家长的小贴士:

  • 元组和列表的主要区别是“可变vs不可变“
  • 用“相册“类比:元组是洗出来的照片,列表是电子相册
  • 强调元组的不可变性,这是它存在的意义
  • 元组通常用于“不应该被修改“的数据

常见错误和调试

错误1:索引越界

错误代码:

fruits = ["苹果", "香蕉", "橙子"]
print(fruits[3])  # IndexError!

原因:索引3不存在(有效索引是0, 1, 2)

修正:

fruits = ["苹果", "香蕉", "橙子"]

# 方法1:检查索引
if len(fruits) > 3:
    print(fruits[3])
else:
    print("索引3不存在")

# 方法2:使用反向索引
print(fruits[-1])  # 最后一个元素

错误2:缩进错误

错误代码:

fruits = ["苹果", "香蕉", "橙子"]
for fruit in fruits:
print(fruit)  # IndentationError!

原因:for循环的循环体必须缩进

修正:

fruits = ["苹果", "香蕉", "橙子"]
for fruit in fruits:
    print(fruit)  # 正确缩进

错误3:修改正在遍历的列表

问题代码:

numbers = [1, 2, 3, 4, 5]
for num in numbers:
    if num % 2 == 0:
        numbers.remove(num)  # 危险!
print(numbers)  # 结果可能不正确

原因:遍历时修改列表会导致索引混乱

修正:

# 方法1:创建新列表
numbers = [1, 2, 3, 4, 5]
odd_numbers = []
for num in numbers:
    if num % 2 != 0:  # 只保留奇数
        odd_numbers.append(num)
numbers = odd_numbers
print(numbers)  # [1, 3, 5]

# 方法2:遍历副本
numbers = [1, 2, 3, 4, 5]
for num in numbers[:]:  # 遍历副本
    if num % 2 == 0:
        numbers.remove(num)
print(numbers)  # [1, 3, 5]

错误4:混淆append和extend

错误代码:

list1 = [1, 2]
list2 = [3, 4]
list1.append(list2)  # 本想合并,结果嵌套了
print(list1)  # [1, 2, [3, 4]](不是想要的!)

修正:

list1 = [1, 2]
list2 = [3, 4]

# 方法1:使用extend
list1.extend(list2)
print(list1)  # [1, 2, 3, 4]

# 方法2:使用+
list1 = [1, 2]
list2 = [3, 4]
list3 = list1 + list2
print(list3)  # [1, 2, 3, 4]

给家长的小贴士:

  • 索引越界是最常见的错误,教孩子学会检查长度
  • 缩进错误是初学者常犯的,强调Python对缩进的要求
  • 遍历时修改列表是高级错误,建议使用新列表
  • 鼓励孩子使用print调试法:打印中间结果

章节小结

我们学到了什么

  1. 列表的概念:

    • 列表是有序的数据集合
    • 列表用方括号[]表示
    • 列表可以存储任意类型的数据
    • 列表是可变的(可以修改)
  2. 列表在内存中的存储:

    • 列表在内存中连续存放
    • 索引是相对于起始位置的偏移量
    • 通过索引可以快速访问元素
    • 列表长度影响内存占用
  3. 列表的基本操作:

    • 创建列表:[]list()
    • 访问元素:使用索引(从0开始)
    • 切片:list[start:end:step]
    • 获取长度:len(list)
  4. 列表的方法:

    • 添加元素:append(), insert(), extend()
    • 删除元素:del, remove(), pop()
    • 排序:sort(), sorted()
    • 其他:count(), index(), reverse()
  5. 遍历列表:

    • 遍历元素:for item in list
    • 遍历索引:for i in range(len(list))
    • 同时获取索引和元素:enumerate(list)
  6. 数学应用:

    • 统计量:平均数、中位数、众数、最大值、最小值
    • 数列:等差数列、等比数列、斐波那契数列
    • 数据排序和查找
  7. 字符串和列表:

    • 字符串是特殊的列表
    • 字符串不可变,列表可变
    • 转换:list()"".join()
  8. 元组:

    • 元组是不可变的列表
    • 元组用圆括号()表示
    • 元组用于存储固定数据

重要概念对比

概念列表元组字符串
符号[]()""''
可变性可变不可变不可变
索引支持支持支持
切片支持支持支持
遍历支持支持支持
元素类型任意任意仅字符
内存存储连续连续连续

常用操作总结

操作语法示例
创建[]list()fruits = ["苹果", "香蕉"]
访问list[index]fruits[0]
切片list[start:end]fruits[1:3]
长度len(list)len(fruits)
添加list.append(item)fruits.append("橙子")
删除del list[index]del fruits[0]
排序list.sort()numbers.sort()
遍历for item in listfor fruit in fruits

编程技巧

  1. 选择合适的遍历方式:

    • 只需要访问元素:遍历元素
    • 需要修改元素:遍历索引
    • 需要索引和元素:使用enumerate
  2. 避免常见错误:

    • 索引从0开始,不是1
    • 切片不包括结束位置
    • 遍历时不要直接修改列表
  3. 善用列表方法:

    • innot in判断元素是否存在
    • sum(), max(), min()快速统计
    • split()join()处理字符串
  4. 理解内存原理:

    • 列表在内存中连续存放
    • 索引用于快速计算内存地址
    • 列表大小与内存占用相关

下一步

在下一章(第11章),我们将学习字典(Dictionary),这是一种用“键-值“对存储数据的数据结构。字典不像列表那样连续存储,而是使用一种叫做“哈希表“的技术,能快速查找数据!

挑战练习

  1. 必做:

    • 创建一个存储你朋友名字的列表,并打印每个名字
    • 给定一个成绩列表,计算平均分、最高分、最低分
    • 将一个列表的所有元素乘以2
  2. 选做:

    • 实现一个简单的待办事项列表程序
    • 统计一个句子中每个单词出现的次数
    • 合并两个列表并去除重复元素
  3. 数学挑战:

    • 实现1+2+3+…+100的求和(用循环)
    • 生成前20项斐波那契数列
    • 找出一个列表的中位数
    • 统计数据的众数
  4. 综合项目:

    • 设计一个学生成绩管理系统(使用列表存储学生信息)
    • 实现一个简单的井字棋游戏(使用列表表示棋盘)
    • 创建一个单词统计程序(统计文本中的单词频率)

加油!你已经掌握了Python中最常用的数据结构——列表!🎉

列表是程序员的“瑞士军刀“,几乎所有的Python程序都会用到列表。你现在不仅学会了如何使用列表,还理解了它在内存中的存储方式,以及如何用它来解决数学问题!

继续努力,接下来我们将学习字典,它会让你的程序更强大!💪

第11章 数据结构:字典

引言

想象一下,你在学校里有一个储物柜。每个储物柜都有一个编号,你可以在里面放东西。要用储物柜,你需要记住你的柜子号码,这样就能快速找到你的东西。

或者想想你查字典的时候:你想找“苹果“这个词,你会先找拼音“p“,然后在这个字母下面找“ping“,最后找到“苹果“。这个过程就是通过“索引“(拼音)来找到“内容“(解释)。

在Python中,有一种和储物柜很像的数据结构,叫做“字典“(Dictionary)。字典让我们能够用“钥匙“(key)来找到对应的“值“(value),就像用储物柜的号码来找到里面的东西一样。

🎯 本章学习目标

  • 学会使用字典存储“键-值“对数据
  • 理解字典和数学中“映射“的关系
  • 了解字典为什么查找速度这么快(哈希表)
  • 用字典解决实际问题

给家长的小贴士 🧑‍🏫

生活类比:可以用以下例子帮助孩子理解“键-值“对:

  • 电话簿:姓名→电话号码
  • 字典查词:拼音→词语解释
  • 储物柜:柜号→存放物品
  • 身份证号:号码→个人信息
  • 快递柜:取件码→快递包裹

数学关联:字典的概念和数学中的“映射“或“函数“非常相似!如果孩子学过函数 y = f(x),可以告诉孩子:

  • 键(key)就是自变量 x
  • 值(value)就是因变量 y
  • 字典就是一组特殊的函数关系

什么是字典

字典是Python中的一种数据结构,它存储的是“键-值“对(key-value pairs)。

键和值的概念

  • 键(key):就像储物柜的编号,用来唯一标识一个值
  • 值(value):就像储物柜里放的东西,是我们真正想要存储的数据

数学中的“映射“ 📊

在数学课中,你可能学过映射或者函数的概念。比如:

  • 如果 x = 1,那么 y = 2
  • 如果 x = 2,那么 y = 4
  • 如果 x = 3,那么 y = 6

这可以写成函数:y = 2x

在Python的字典中,我们也可以表达类似的关系:

# 创建一个字典
heights = {"小明": 150, "小红": 145, "小华": 152}

# 访问字典中的值
print(heights["小明"])  # 输出: 150

在这个例子中:

  • “小明”、“小红”、“小华” 是键(key),就像数学中的 x
  • 150、145、152 是值(value),就像数学中的 y
  • 整个 {"小明": 150, "小红": 145, "小华": 152} 就是一组映射关系

给家长的小贴士 🧑‍🏫

数学融入:可以和孩子一起做以下练习:

  1. 让孩子写出班级同学的姓名和身高的对应关系
  2. 问问孩子:这像不像数学中的“有序数对“?
  3. 引导孩子理解:(姓名, 身高) 就像 (x, y) 这样的坐标

思考问题

  • 如果两个同学叫同一个名字,会发生什么?(引导学生理解键的唯一性)
  • 如果想查询某个同学的身高,需要做什么?(引导学生理解查找的概念)

字典与列表的区别

特点列表字典
索引数字索引(0, 1, 2…)任意不可变类型的键
访问list[0]dict["key"]
顺序有序无序(Python 3.7+有序)
用途按顺序存储数据按键值对存储数据

给家长的小贴士:可以告诉孩子,列表像是一排编号的盒子,你必须记住号码才能找到东西;而字典像是给每个盒子贴了标签,你可以直接通过标签找到东西。


为什么字典查找这么快?⚡

你可能会问:列表和字典都可以存储数据,为什么字典的查找速度更快呢?

列表的查找方式

想象一下,你要在一本没有索引的书中找某个词。你必须:

  1. 从第一页开始
  2. 一页一页地翻
  3. 直到找到这个词

如果书有100页,你平均要翻50页才能找到!

在Python中,用列表查找也是这样:

# 用列表存储学生信息
students_list = ["小明", "小红", "小华", "小刚", "小李"]

# 查找"小华"
if "小华" in students_list:
    print("找到了!")
# Python需要从第一个开始,一个一个比较,直到找到"小华"

字典的查找方式:哈希表 🗂️

字典使用了一种叫做哈希表(Hash Table)的技术,它就像给每个数据都贴了一个“魔法标签“。

什么是哈希函数?

哈希函数就像一个“魔法计算器“,它能:

  • 把任意输入(比如“小明“)转换成一个数字(比如 23)
  • 相同的输入总是得到相同的数字(“小明“永远是23)
  • 不同的输入大概率得到不同的数字
# 简化版的哈希函数(用取余数来模拟)
def simple_hash(key, size):
    """简化版哈希函数:把字符串转成数字,然后取余"""
    # 在真实Python中,hash()函数要复杂得多
    total = 0
    for char in key:
        total += ord(char)  # 把字符转成数字
    return total % size

# 假设我们有10个"盒子"
print(simple_hash("小明", 10))  # 输出: 某个0-9的数字
print(simple_hash("小红", 10))  # 输出: 另一个0-9的数字

哈希表的工作原理

想象你有100个储物柜,编号0-99:

  1. 添加数据时

    • 把“小明“通过哈希函数计算,得到 23
    • 把“小明“的信息放在第23号柜子
    • 把“小红“通过哈希函数计算,得到 57
    • 把“小红“的信息放在第57号柜子
  2. 查找数据时

    • 要找“小明“,用哈希函数计算,得到 23
    • 直接去第23号柜子拿
    • 不需要一个个柜子翻!

数学类比:余数的妙用 ➗

哈希函数的核心思想很像“取余数“运算:

问题:有100个苹果,要分给7个人,每人几个?怎么分配?

# 用取余数来分配
apples = ["苹果1", "苹果2", "苹果3", ..., "苹果100"]
people = 7

for i, apple in enumerate(apples):
    person = i % people  # 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, ...
    print(f"{apple} 给第{person}个人")

哈希函数也是类似的思路:把数据通过计算“分配“到不同的位置。

给家长的小贴士 🧑‍🏫

讲解方式

  1. 用快递柜做类比

    • 快递柜有100个格子
    • 你收到取件码“23-5678“
    • 你直接去23号柜子,输入5678就能取件
    • 不需要一个个柜子试!
  2. 时间复杂度(适合有编程基础的家长):

    • 列表查找:O(n),n是元素个数
    • 字典查找:O(1),和元素个数无关
    • 如果有100万个数据,列表平均要找50万次,字典只需要1次!
  3. 实际演示

import time

# 创建大数据
big_list = list(range(100000))
big_dict = {i: f"value_{i}" for i in range(100000)}

# 测试列表查找
start = time.time()
result = 99999 in big_list
list_time = time.time() - start

# 测试字典查找
start = time.time()
result = 99999 in big_dict
dict_time = time.time() - start

print(f"列表查找时间: {list_time:.6f}秒")
print(f"字典查找时间: {dict_time:.6f}秒")
print(f"字典快了 {list_time / dict_time:.0f} 倍!")

注意事项

  • 哈希表虽然快,但需要更多内存空间
  • 不是所有情况都需要字典,数据量小时用列表就够了
  • 小学阶段不需要深入理解哈希算法,知道“它能快速定位“即可

创建字典

方法一:直接创建

# 创建一个空字典
empty_dict = {}

# 创建一个有内容的字典
student = {"name": "小明", "age": 12, "grade": 6}

方法二:使用dict()函数

# 使用dict()函数创建
student = dict(name="小明", age=12, grade=6)

print(student)
# 输出: {'name': '小明', 'age': 12, 'grade': 6}

给家长的小贴士:这两种方法创建的字典是一样的,第一种方法更常用,特别是在处理复杂数据结构时。


访问字典中的值

使用键访问值

scores = {"语文": 95, "数学": 98, "英语": 88}

# 访问语文成绩
print(scores["语文"])  # 输出: 95

# 访问数学成绩
print(scores["数学"])  # 输出: 98

处理键不存在的情况

如果我们访问一个不存在的键,程序会报错:

scores = {"语文": 95, "数学": 98, "英语": 88}

# 这行代码会报错!KeyError
print(scores["科学"])

为了避免这种情况,我们有两种方法:

方法1:使用in检查键是否存在

scores = {"语文": 95, "数学": 98, "英语": 88}

subject = "科学"
if subject in scores:
    print(scores[subject])
else:
    print(f"没有{subject}这门课的成绩")

方法2:使用get()方法

scores = {"语文": 95, "数学": 98, "英语": 88}

# 使用get方法,如果键不存在,返回None
print(scores.get("科学"))  # 输出: None

# 使用get方法,如果键不存在,返回默认值
print(scores.get("科学", "没有这门课"))  # 输出: 没有这门课

给家长的小贴士get()方法更安全,建议孩子养成使用get()的习惯,特别是在处理用户输入时。


添加和修改字典元素

添加新的键值对

# 创建一个空字典
phone_book = {}

# 添加新的联系人
phone_book["小明"] = "123-4567"
phone_book["小红"] = "234-5678"

print(phone_book)
# 输出: {'小明': '123-4567', '小红': '234-5678'}

修改现有的值

scores = {"语文": 95, "数学": 98, "英语": 88}

# 修改语文成绩
scores["语文"] = 99

print(scores)
# 输出: {'语文': 99, '数学': 98, '英语': 88}

给家长的小贴士:在Python中,添加新元素和修改现有元素的语法是一样的。如果键不存在,就是添加;如果键已存在,就是修改。


删除字典元素

方法1:使用del语句

scores = {"语文": 95, "数学": 98, "英语": 88}

# 删除语文成绩
del scores["语文"]

print(scores)
# 输出: {'数学': 98, '英语': 88}

方法2:使用pop()方法

scores = {"语文": 95, "数学": 98, "英语": 88}

# 删除并返回数学成绩
math_score = scores.pop("数学")
print(math_score)  # 输出: 98
print(scores)       # 输出: {'语文': 95, '英语': 88}

方法3:使用popitem()方法

scores = {"语文": 95, "数学": 98, "英语": 88}

# 删除并返回最后一个键值对(Python 3.7+)
last_item = scores.popitem()
print(last_item)  # 输出: ('英语', 88)
print(scores)     # 输出: {'语文': 95, '数学': 98}

给家长的小贴士:提醒孩子,del语句直接删除,不会返回值;pop()方法会返回被删除的值,可以在需要使用这个值的时候使用。


字典的常用操作

获取所有键、所有值、所有键值对

scores = {"语文": 95, "数学": 98, "英语": 88}

# 获取所有键
print(scores.keys())    # 输出: dict_keys(['语文', '数学', '英语'])

# 获取所有值
print(scores.values())  # 输出: dict_values([95, 98, 88])

# 获取所有键值对
print(scores.items())   # 输出: dict_items([('语文', 95), ('数学', 98), ('英语', 88)])

获取字典的长度

scores = {"语文": 95, "数学": 98, "英语": 88}

print(len(scores))  # 输出: 3

检查键是否存在

scores = {"语文": 95, "数学": 98, "英语": 88}

# 使用in检查
print("语文" in scores)   # 输出: True
print("科学" in scores)   # 输出: False

# 使用not in检查
print("语文" not in scores)  # 输出: False

清空字典

scores = {"语文": 95, "数学": 98, "英语": 88}

# 清空字典
scores.clear()

print(scores)  # 输出: {}

给家长的小贴士keys()values()items()返回的是视图对象,不是列表。如果需要列表,可以用list()转换:list(scores.keys())


遍历字典

遍历所有键

scores = {"语文": 95, "数学": 98, "英语": 88}

# 方法1:直接遍历字典(遍历的是键)
for subject in scores:
    print(subject)

# 输出:
# 语文
# 数学
# 英语

# 方法2:使用keys()方法(更明确)
for subject in scores.keys():
    print(subject)

遍历所有值

scores = {"语文": 95, "数学": 98, "英语": 88}

for score in scores.values():
    print(score)

# 输出:
# 95
# 98
# 88

遍历所有键值对

scores = {"语文": 95, "数学": 98, "英语": 88}

# 使用items()方法同时获取键和值
for subject, score in scores.items():
    print(f"{subject}: {score}分")

# 输出:
# 语文: 95分
# 数学: 98分
# 英语: 88分

给家长的小贴士:最常用的是items()方法,因为它能同时获取键和值,代码更简洁。


字典的常用方法

update()方法 - 更新字典

# 原字典
scores = {"语文": 95, "数学": 98}

# 新数据
new_scores = {"英语": 88, "数学": 100}

# 用new_scores更新scores
scores.update(new_scores)

print(scores)
# 输出: {'语文': 95, '数学': 100, '英语': 88}

setdefault()方法 - 设置默认值

scores = {"语文": 95, "数学": 98}

# 如果"英语"不存在,设置默认值85
english_score = scores.setdefault("英语", 85)
print(english_score)  # 输出: 85
print(scores)         # 输出: {'语文': 95, '数学': 98, '英语': 85}

# 如果"数学"已存在,不会修改
math_score = scores.setdefault("数学", 0)
print(math_score)     # 输出: 98
print(scores)         # 输出: {'语文': 95, '数学': 98, '英语': 85}

fromkeys()方法 - 创建相同值的字典

# 创建一个字典,所有值都是0
subjects = ["语文", "数学", "英语"]
scores = dict.fromkeys(subjects, 0)

print(scores)
# 输出: {'语文': 0, '数学': 0, '英语': 0}

copy()方法 - 复制字典

original = {"语文": 95, "数学": 98}

# 浅复制
copied = original.copy()

# 修改复制的字典
copied["英语"] = 88

print(original)  # 输出: {'语文': 95, '数学': 98}
print(copied)    # 输出: {'语文': 95, '数学': 98, '英语': 88}

给家长的小贴士copy()方法很重要,因为它创建了一个独立的副本,不会影响原字典。直接赋值new_dict = old_dict只是创建了一个引用。


字典的键的要求

字典的键必须满足以下要求:

  1. 键必须是不可变类型

    • 可以是:整数、浮点数、字符串、元组
    • 不能是:列表、字典
  2. 键必须是唯一的

    • 如果有重复的键,后面的值会覆盖前面的值
# 正确的键
dict1 = {1: "one", 2.5: "two point five", "hello": "world", (1, 2): "tuple key"}

# 错误的键(会报错)
# dict2 = {[1, 2]: "list key"}  # TypeError: 列表不能作为键

# 重复的键(后面的值会覆盖前面的)
dict3 = {"name": "小明", "name": "小红"}
print(dict3)  # 输出: {'name': '小红'}

给家长的小贴士:可以用“门牌号“来类比,门牌号是固定的、唯一的,就像字典的键。如果两个储物柜号码相同,就会造成混乱。


嵌套字典

字典的值可以是任意类型,包括另一个字典。这就是“嵌套字典“。

简单的嵌套字典

# 存储学生信息
student = {
    "name": "小明",
    "age": 12,
    "address": {
        "city": "北京",
        "district": "朝阳区"
    }
}

# 访问嵌套字典
print(student["address"]["city"])  # 输出: 北京

复杂的嵌套字典

# 存储多个学生的信息
students = {
    "001": {
        "name": "小明",
        "age": 12,
        "scores": {"语文": 95, "数学": 98}
    },
    "002": {
        "name": "小红",
        "age": 11,
        "scores": {"语文": 92, "数学": 99}
    }
}

# 访问小红的数学成绩
print(students["002"]["scores"]["数学"])  # 输出: 99

字典和列表的嵌套

# 课程表:字典 + 列表的嵌套
schedule = {
    "星期一": ["语文", "数学", "英语"],
    "星期二": ["数学", "科学", "体育"],
    "星期三": ["语文", "美术", "音乐"]
}

# 访问星期二第二节课
print(schedule["星期二"][1])  # 输出: 科学

复杂的嵌套结构

# 更复杂的课程表:多层嵌套
courses = {
    "MON": {
        "morning": ["数学", "英语", "绘画"],
        "afternoon": ["音乐", "体育"]
    },
    "TUE": {
        "morning": ["科学", "数学", "体育"],
        "afternoon": ["英语"]
    }
}

# 访问星期一上午第二节课
print(courses["MON"]["morning"][1])  # 输出: 英语

# 查看星期一下午课程的类型
print(type(courses["MON"]["afternoon"]))  # 输出: <class 'list'>

# 查看星期一上午第二节课的类型
print(type(courses["MON"]["morning"][1]))  # 输出: <class 'str'>

给家长的小贴士:嵌套结构理解起来比较困难,建议用“俄罗斯套娃“或“多层盒子“的类比。访问嵌套数据就像一层层打开盒子,每次只打开一层。


字典的排序

按键排序

scores = {"语文": 95, "数学": 98, "英语": 88}

# 按键排序
sorted_keys = sorted(scores.keys())
print(sorted_keys)  # 输出: ['数学', '英语', '语文']

# 按排序后的顺序遍历
for subject in sorted_keys:
    print(f"{subject}: {scores[subject]}")

# 输出:
# 数学: 98
# 英语: 88
# 语文: 95

按值排序

scores = {"语文": 95, "数学": 98, "英语": 88}

# 按值排序(从低到高)
sorted_items = sorted(scores.items(), key=lambda x: x[1])
print(sorted_items)  # 输出: [('英语', 88), ('语文', 95), ('数学', 98)]

# 按值排序(从高到低)
sorted_items = sorted(scores.items(), key=lambda x: x[1], reverse=True)
print(sorted_items)  # 输出: [('数学', 98), ('语文', 95), ('英语', 88)]

给家长的小贴士:lambda表达式对孩子来说比较抽象,可以简单地解释为“告诉Python按什么排序“。对于初学者,可以先跳过这个部分。


字典的应用案例

案例1:数学函数计算器 🧮

我们可以用字典来存储数学函数的值:

# 创建一个简单的函数表
function_table = {
    1: 2,    # f(1) = 2
    2: 4,    # f(2) = 4
    3: 6,    # f(3) = 6
    4: 8,    # f(4) = 8
    5: 10    # f(5) = 10
}

# 查询函数值
x = int(input("请输入x的值(1-5): "))
if x in function_table:
    y = function_table[x]
    print(f"f({x}) = {y}")
else:
    print("这个值不在表中")

# 你能猜出这是什么函数吗?
# 答案:f(x) = 2x (乘法函数)

数学挑战:发现规律 📊

# 更复杂的函数表
math_functions = {
    "正方形面积": {1: 1, 2: 4, 3: 9, 4: 16, 5: 25},
    "正方形周长": {1: 4, 2: 8, 3: 12, 4: 16, 5: 20},
    "圆的面积": {1: 3.14, 2: 12.56, 3: 28.26, 4: 50.24, 5: 78.5}
}

# 让用户选择函数
for name in math_functions:
    print(f"- {name}")

choice = input("\n请选择一个函数: ")
if choice in math_functions:
    print(f"\n{choice}的值:")
    for x, y in math_functions[choice].items():
        print(f"  当边长/半径 = {x} 时,结果 = {y}")

    # 让用户猜规律
    print("\n你能发现这些数字的规律吗?")

给家长的小贴士 🧑‍🏫

数学融入

  1. 函数关系

    • 引导孩子观察输入和输出的关系
    • 问:“当x增加1时,y增加多少?”
    • 这就是学习“变化率“的早期概念
  2. 找规律

    • 正方形面积:1, 4, 9, 16, 25 → 这些是什么数的平方?
    • 正方形周长:4, 8, 12, 16, 20 → 每次增加4
    • 引导孩子发现:面积 = 边长²,周长 = 边长 × 4
  3. 扩展练习

    • 让孩子自己创建一个函数表
    • 比如:f(x) = x + 3,f(x) = x × 2 + 1
    • 用字典存储前10个值

案例2:制作简单的翻译器

# 英汉词典
dictionary = {
    "hello": "你好",
    "world": "世界",
    "python": "蟒蛇",
    "computer": "电脑"
}

# 翻译功能
word = input("请输入要翻译的英文单词: ")
translation = dictionary.get(word.lower(), "词典里没有这个单词")

print(f"翻译结果: {translation}")

案例2:统计字符出现次数

# 统计字符串中每个字符出现的次数
text = "hello world"

# 创建空字典
char_count = {}

# 遍历字符串
for char in text:
    if char in char_count:
        char_count[char] += 1
    else:
        char_count[char] = 1

print(char_count)
# 输出: {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}

案例3:制作简单的投票系统

# 投票系统
votes = {}

while True:
    name = input("请输入要投票的同学的名字(输入'结束'退出): ")

    if name == "结束":
        break

    # 使用get()方法,如果不存在则从0开始计数
    votes[name] = votes.get(name, 0) + 1
    print(f"{name}的票数: {votes[name]}")

# 输出最终结果
print("\n投票结果:")
for name, count in votes.items():
    print(f"{name}: {count}票")

案例4:学生成绩管理系统

# 学生成绩管理系统
students = {}

while True:
    print("\n===== 成绩管理系统 =====")
    print("1. 添加学生")
    print("2. 查询学生")
    print("3. 显示所有学生")
    print("4. 退出")

    choice = input("请选择操作 (1-4): ")

    if choice == "1":
        # 添加学生
        name = input("请输入学生姓名: ")
        score = int(input("请输入学生成绩: "))
        students[name] = score
        print(f"已添加: {name}, 成绩: {score}")

    elif choice == "2":
        # 查询学生
        name = input("请输入要查询的学生姓名: ")
        if name in students:
            print(f"{name}的成绩是: {students[name]}")
        else:
            print("没有找到该学生")

    elif choice == "3":
        # 显示所有学生
        if students:
            print("\n所有学生成绩:")
            for name, score in students.items():
                print(f"{name}: {score}分")
        else:
            print("还没有添加学生")

    elif choice == "4":
        print("再见!")
        break

    else:
        print("无效的选择,请重新输入")

给家长的小贴士:这些案例都是很好的综合练习,可以让孩子自己尝试实现,然后再和示例代码对比,找出差异。


实践练习

练习1:单位换算器 📏

创建一个单位换算程序,支持长度、重量、温度等单位的换算。

数学知识

  • 1米 = 100厘米
  • 1千克 = 1000克
  • 摄氏度转华氏度:F = C × 9/5 + 32
查看答案
# 单位换算器
conversions = {
    "长度": {"米": 1, "厘米": 0.01, "毫米": 0.001},
    "重量": {"千克": 1, "克": 0.001, "吨": 1000},
    "温度": {"摄氏度": "C", "华氏度": "F"}
}

print("=== 单位换算器 ===")
print("1. 长度换算")
print("2. 重量换算")
print("3. 温度换算")

choice = input("请选择换算类型 (1-3): ")

if choice == "1":
    # 长度换算
    value = float(input("请输入数值: "))
    unit = input("请输入单位 (米/厘米/毫米): ")
    target = input("请输入目标单位 (米/厘米/毫米): ")

    if unit in conversions["长度"] and target in conversions["长度"]:
        # 先转为米,再转为目标单位
        in_meters = value * conversions["长度"][unit]
        result = in_meters / conversions["长度"][target]
        print(f"{value}{unit} = {result}{target}")

elif choice == "2":
    # 重量换算(类似逻辑)
    value = float(input("请输入数值: "))
    unit = input("请输入单位 (千克/克/吨): ")
    target = input("请输入目标单位 (千克/克/吨): ")

    if unit in conversions["重量"] and target in conversions["重量"]:
        in_kg = value * conversions["重量"][unit]
        result = in_kg / conversions["重量"][target]
        print(f"{value}{unit} = {result}{target}")

elif choice == "3":
    # 温度换算
    value = float(input("请输入温度值: "))
    unit = input("请输入单位 (摄氏度/华氏度): ")

    if unit == "摄氏度":
        # C to F: F = C × 9/5 + 32
        result = value * 9/5 + 32
        print(f"{value}°C = {result:.1f}°F")
    elif unit == "华氏度":
        # F to C: C = (F - 32) × 5/9
        result = (value - 32) * 5/9
        print(f"{value}°F = {result:.1f}°C")

数学思考

  • 为什么要用字典存储单位换算率?(键值对的对应关系)
  • 温度换算为什么不能用简单的乘法?(引导理解线性函数 y = ax + b)

练习2:罗马数字转换器 ➗⚖️

罗马数字是古罗马人使用的数字系统,比如:I=1, V=5, X=10, L=50, C=100。

创建一个程序,在罗马数字和阿拉伯数字之间转换。

查看答案
# 罗马数字转换器
roman_to_arabic = {
    "I": 1,
    "V": 5,
    "X": 10,
    "L": 50,
    "C": 100,
    "D": 500,
    "M": 1000
}

print("=== 罗马数字转换器 ===")
print("1. 罗马数字 → 阿拉伯数字")
print("2. 阿拉伯数字 → 罗马数字")

choice = input("请选择 (1-2): ")

if choice == "1":
    # 罗马数字转阿拉伯数字
    roman = input("请输入罗马数字 (大写,如 XIV): ")
    total = 0
    prev_value = 0

    # 从右到左遍历
    for char in reversed(roman):
        value = roman_to_arabic[char]
        if value < prev_value:
            total -= value
        else:
            total += value
        prev_value = value

    print(f"{roman} = {total}")

elif choice == "2":
    # 简化版:阿拉伯数字转罗马数字(仅限1-10)
    arabic_to_roman = {
        1: "I", 2: "II", 3: "III", 4: "IV", 5: "V",
        6: "VI", 7: "VII", 8: "VIII", 9: "IX", 10: "X"
    }

    num = int(input("请输入数字 (1-10): "))
    if num in arabic_to_roman:
        print(f"{num} = {arabic_to_roman[num]}")
    else:
        print("这个版本只支持1-10的转换")

历史和数学

  • 罗马数字为什么后来被阿拉伯数字取代?(计算方便)
  • 罗马数字的“减法原则“:IV = 5-1 = 4,这体现了什么数学思想?

练习3:简单的电话簿

创建一个电话簿程序,支持以下功能:

  1. 添加联系人(姓名和电话)
  2. 查询联系人
  3. 显示所有联系人
  4. 删除联系人
查看答案
# 电话簿程序
phone_book = {}

while True:
    print("\n===== 电话簿 =====")
    print("1. 添加联系人")
    print("2. 查询联系人")
    print("3. 显示所有联系人")
    print("4. 删除联系人")
    print("5. 退出")

    choice = input("请选择操作 (1-5): ")

    if choice == "1":
        # 添加联系人
        name = input("请输入姓名: ")
        phone = input("请输入电话: ")
        phone_book[name] = phone
        print(f"已添加: {name}")

    elif choice == "2":
        # 查询联系人
        name = input("请输入要查询的姓名: ")
        if name in phone_book:
            print(f"{name}的电话: {phone_book[name]}")
        else:
            print("没有找到该联系人")

    elif choice == "3":
        # 显示所有联系人
        if phone_book:
            print("\n所有联系人:")
            for name, phone in phone_book.items():
                print(f"{name}: {phone}")
        else:
            print("电话簿是空的")

    elif choice == "4":
        # 删除联系人
        name = input("请输入要删除的联系人姓名: ")
        if name in phone_book:
            del phone_book[name]
            print(f"已删除: {name}")
        else:
            print("没有找到该联系人")

    elif choice == "5":
        print("再见!")
        break

    else:
        print("无效的选择,请重新输入")

练习2:单词统计器

编写一个程序,统计一段文本中每个单词出现的次数。

查看答案
# 单词统计器
text = input("请输入一段文本: ")

# 分割成单词
words = text.split()

# 统计词频
word_count = {}
for word in words:
    word_count[word] = word_count.get(word, 0) + 1

# 输出结果
print("\n单词出现次数:")
for word, count in sorted(word_count.items()):
    print(f"{word}: {count}次")

练习3:制作课程表

创建一个程序,管理一周的课程表。

查看答案
# 课程表管理
schedule = {}

weekdays = ["星期一", "星期二", "星期三", "星期四", "星期五"]

# 初始化课程表
for day in weekdays:
    schedule[day] = []

while True:
    print("\n===== 课程表管理 =====")
    print("1. 添加课程")
    print("2. 查看课程表")
    print("3. 退出")

    choice = input("请选择操作 (1-3): ")

    if choice == "1":
        # 添加课程
        for i, day in enumerate(weekdays):
            print(f"{i+1}. {day}")

        day_choice = int(input("请选择星期几 (1-5): ")) - 1
        course = input("请输入课程名称: ")
        schedule[weekdays[day_choice]].append(course)
        print(f"已添加: {weekdays[day_choice]} - {course}")

    elif choice == "2":
        # 查看课程表
        print("\n=== 本周课程表 ===")
        for day in weekdays:
            courses = schedule[day]
            if courses:
                courses_str = "、".join(courses)
                print(f"{day}: {courses_str}")
            else:
                print(f"{day}: 无课程")

    elif choice == "3":
        print("再见!")
        break

    else:
        print("无效的选择,请重新输入")

练习4:计算平均成绩

创建一个程序,管理多个学生的成绩,并计算平均分。

查看答案
# 成绩管理系统
students = {}

while True:
    print("\n===== 成绩管理 =====")
    print("1. 添加学生成绩")
    print("2. 查看学生成绩")
    print("3. 查看班级平均分")
    print("4. 退出")

    choice = input("请选择操作 (1-4): ")

    if choice == "1":
        # 添加学生
        name = input("请输入学生姓名: ")
        score = float(input("请输入成绩: "))
        students[name] = score
        print(f"已添加: {name}")

    elif choice == "2":
        # 查看成绩
        if students:
            print("\n所有学生成绩:")
            for name, score in students.items():
                print(f"{name}: {score}分")
        else:
            print("还没有添加学生")

    elif choice == "3":
        # 计算平均分
        if students:
            average = sum(students.values()) / len(students)
            print(f"班级平均分: {average:.2f}")
        else:
            print("还没有添加学生")

    elif choice == "4":
        print("再见!")
        break

    else:
        print("无效的选择,请重新输入")

练习5:简单的加密解密程序

创建一个简单的凯撒密码加密解密程序。

查看答案
# 凯撒密码加密解密
def create_cipher(shift):
    """创建密码本"""
    cipher = {}
    for i in range(26):
        original = chr(ord('a') + i)
        encrypted = chr(ord('a') + (i + shift) % 26)
        cipher[original] = encrypted
        cipher[original.upper()] = encrypted.upper()
    return cipher

def encrypt(text, cipher):
    """加密"""
    result = []
    for char in text:
        if char in cipher:
            result.append(cipher[char])
        else:
            result.append(char)
    return ''.join(result)

def decrypt(text, cipher):
    """解密"""
    # 创建反向密码本
    reverse_cipher = {v: k for k, v in cipher.items()}
    return encrypt(text, reverse_cipher)

# 主程序
shift = int(input("请输入偏移量 (1-25): "))
cipher = create_cipher(shift)

while True:
    print("\n===== 加密解密系统 =====")
    print("1. 加密")
    print("2. 解密")
    print("3. 退出")

    choice = input("请选择操作 (1-3): ")

    if choice == "1":
        text = input("请输入要加密的文本: ")
        encrypted = encrypt(text, cipher)
        print(f"加密结果: {encrypted}")

    elif choice == "2":
        text = input("请输入要解密的文本: ")
        decrypted = decrypt(text, cipher)
        print(f"解密结果: {decrypted}")

    elif choice == "3":
        print("再见!")
        break

    else:
        print("无效的选择,请重新输入")

练习6:数学成绩分析 📊

创建一个程序,分析班级数学成绩,计算最高分、最低分、平均分,并统计各分数段的人数。

查看答案
# 数学成绩分析
students = {
    "小明": 95,
    "小红": 87,
    "小华": 92,
    "小刚": 78,
    "小李": 88,
    "小张": 65,
    "小王": 90,
    "小陈": 82,
    "小刘": 76,
    "小赵": 94
}

# 计算统计数据
scores = list(students.values())
max_score = max(scores)
min_score = min(scores)
avg_score = sum(scores) / len(scores)

print(f"=== 数学成绩分析 ===")
print(f"最高分: {max_score}")
print(f"最低分: {min_score}")
print(f"平均分: {avg_score:.2f}")

# 按分数段统计
grade_ranges = {
    "优秀(90-100)": 0,
    "良好(80-89)": 0,
    "及格(60-79)": 0,
    "不及格(0-59)": 0
}

for score in scores:
    if score >= 90:
        grade_ranges["优秀(90-100)"] += 1
    elif score >= 80:
        grade_ranges["良好(80-89)"] += 1
    elif score >= 60:
        grade_ranges["及格(60-79)"] += 1
    else:
        grade_ranges["不及格(0-59)"] += 1

print("\n分数段统计:")
for range_name, count in grade_ranges.items():
    print(f"{range_name}: {count}人")

# 找出成绩最高和最低的学生
max_student = max(students, key=students.get)
min_student = min(students, key=students.get)

print(f"\n最高分学生: {max_student} ({students[max_student]}分)")
print(f"最低分学生: {min_student} ({students[min_student]}分)")

数学知识点

  • 平均数:所有分数的和 ÷ 人数
  • 中位数:如果按分数排序,中间位置的分数
  • 众数:出现次数最多的分数
  • 极差:最高分 - 最低分

扩展挑战

  • 计算方差(衡量分数的分散程度)
  • 画出分数分布的柱状图
  • 比较不同班级的成绩

综合应用:人员信息管理系统

结合前面学的知识,我们创建一个完整的人员信息管理系统:

# 人员信息管理系统
people = {}

def add_person():
    """添加人员"""
    name = input("请输入姓名: ")
    age = input("请输入年龄: ")
    phone = input("请输入电话: ")

    people[name] = {
        "age": age,
        "phone": phone
    }
    print(f"已添加: {name}")

def find_person():
    """查找人员"""
    name = input("请输入要查找的姓名: ")
    if name in people:
        info = people[name]
        print(f"姓名: {name}")
        print(f"年龄: {info['age']}")
        print(f"电话: {info['phone']}")
    else:
        print("没有找到该人员")

def list_all():
    """列出所有人员"""
    if people:
        print("\n所有人员信息:")
        print("-" * 40)
        for name, info in people.items():
            print(f"姓名: {name}")
            print(f"年龄: {info['age']}")
            print(f"电话: {info['phone']}")
            print("-" * 40)
    else:
        print("还没有添加人员")

def delete_person():
    """删除人员"""
    name = input("请输入要删除的人员姓名: ")
    if name in people:
        del people[name]
        print(f"已删除: {name}")
    else:
        print("没有找到该人员")

# 主菜单
while True:
    print("\n===== 人员信息管理系统 =====")
    print("1. 添加人员")
    print("2. 查找人员")
    print("3. 列出所有人员")
    print("4. 删除人员")
    print("5. 退出")

    choice = input("请选择操作 (1-5): ")

    if choice == "1":
        add_person()
    elif choice == "2":
        find_person()
    elif choice == "3":
        list_all()
    elif choice == "4":
        delete_person()
    elif choice == "5":
        print("再见!")
        break
    else:
        print("无效的选择,请重新输入")

给家长的小贴士:这个综合项目使用了字典的嵌套、增删改查等所有核心概念,是很好的实战练习。


字典和列表的选择

什么时候使用列表?

  • 需要按顺序存储数据
  • 使用数字索引访问元素
  • 数据之间没有明确的“键“关系
  • 例如:购物清单、待办事项列表

什么时候使用字典?

  • 需要用有意义的名称访问数据
  • 数据有明确的“键-值“关系
  • 需要快速查找和更新
  • 例如:电话簿、成绩表、配置信息

对比示例

# 使用列表存储学生信息(不推荐)
student_list = ["小明", 12, 6, 95]  # 难以记住每个位置的含义

# 使用字典存储学生信息(推荐)
student_dict = {
    "name": "小明",
    "age": 12,
    "grade": 6,
    "score": 95
}  # 每个字段都有明确的含义

给家长的小贴士:选择合适的数据结构是编程的重要能力,鼓励孩子在编写程序前先思考:“用列表还是字典更合适?”


常见错误和调试

错误1:使用不存在的键

scores = {"语文": 95, "数学": 98}

# 错误:直接访问不存在的键
print(scores["英语"])  # KeyError: '英语'

# 正确:使用get()方法
print(scores.get("英语", "没有这门课"))

错误2:使用可变类型作为键

# 错误:列表不能作为键
# my_dict = {[1, 2]: "value"}  # TypeError

# 正确:元组可以作为键
my_dict = {(1, 2): "value"}

错误3:遍历字典时修改字典

scores = {"语文": 95, "数学": 98, "英语": 88}

# 错误:遍历时修改字典
for subject in scores:
    if scores[subject] < 90:
        del scores[subject]  # RuntimeError

# 正确:先收集要删除的键,再删除
to_remove = [subject for subject in scores if scores[subject] < 90]
for subject in to_remove:
    del scores[subject]

错误4:混淆赋值和copy()

original = {"语文": 95, "数学": 98}

# 错误:这只是创建了一个引用
copied = original
copied["英语"] = 88
print(original)  # original也被修改了!

# 正确:使用copy()创建独立副本
copied = original.copy()
copied["英语"] = 88
print(original)  # original不受影响

调试技巧

  1. 打印字典内容
scores = {"语文": 95, "数学": 98}
print(scores)  # 查看完整字典
  1. 检查键是否存在
if "数学" in scores:
    print("数学成绩存在")
  1. 使用type()检查类型
print(type(scores))  # 确认是字典类型

给家长的小贴士:字典的错误通常比较隐蔽,建议在调试时多使用print()语句,逐步检查每一步的执行结果。


性能考虑

字典的查找速度

字典的查找速度非常快(O(1)),而列表的查找速度较慢(O(n))。

# 使用列表查找(慢)
students_list = ["小明", "小红", "小华", "小刚", "小李"]
if "小华" in students_list:  # 需要逐个比较
    print("找到了")

# 使用字典查找(快)
students_dict = {"小明": 1, "小红": 2, "小华": 3}
if "小华" in students_dict:  # 直接找到
    print("找到了")

什么时候考虑性能?

  • 数据量很大时(比如超过1000个元素)
  • 需要频繁查找时
  • 对性能要求高的程序

给家长的小贴士:对于小学阶段的学习,性能不是重点,但了解这个知识点有助于培养良好的编程习惯。


字典的高级应用

使用字典作为缓存

# 斐波那契数列(带缓存)
cache = {}

def fib(n):
    if n in cache:
        return cache[n]

    if n <= 1:
        result = n
    else:
        result = fib(n-1) + fib(n-2)

    cache[n] = result
    return result

print(fib(100))  # 计算很快,因为有缓存

使用字典统计

# 统计单词词频
text = "hello world hello python world"

words = text.split()
word_count = {}

for word in words:
    word_count[word] = word_count.get(word, 0) + 1

print(word_count)
# 输出: {'hello': 2, 'world': 2, 'python': 1}

使用字典分组

# 按成绩分组
students = [
    {"name": "小明", "score": 95},
    {"name": "小红", "score": 87},
    {"name": "小华", "score": 92}
]

# 按分数段分组
groups = {"优秀": [], "良好": [], "及格": []}

for student in students:
    score = student["score"]
    if score >= 90:
        groups["优秀"].append(student["name"])
    elif score >= 80:
        groups["良好"].append(student["name"])
    else:
        groups["及格"].append(student["name"])

print(groups)

给家长的小贴士:这些高级应用可能比较复杂,可以让孩子先掌握基础,等有更多编程经验后再学习。


字典 vs 其他数据结构

数据结构特点适用场景
列表有序、可重复存储序列数据
元组有序、不可变固定不变的数据
集合无序、唯一去重、集合运算
字典键值对快速查找、关联数据
# 列表:购物清单
shopping_list = ["苹果", "香蕉", "橙子"]

# 元组:固定坐标
point = (10, 20)

# 集合:去重
numbers = {1, 2, 3, 2, 1}  # {1, 2, 3}

# 字典:成绩表
scores = {"小明": 95, "小红": 98}

给家长的小贴士:让孩子理解不同数据结构的特点和适用场景,有助于选择合适的工具。


章节小结

核心知识点

  1. 字典的基本概念

    • 字典是键值对的集合
    • 键必须是不可变类型且唯一
    • 值可以是任意类型
  2. 字典与数学的关联 📊:

    • 字典就像数学中的“映射“关系
    • 键(key)相当于自变量 x
    • 值(value)相当于因变量 y
    • 字典表达了“一对一“的对应关系
  3. 字典为什么这么快? ⚡:

    • 使用了哈希表技术
    • 哈希函数把键转换成数字(类似取余数)
    • 可以直接定位到存储位置,不需要一个个找
    • 列表查找:O(n),字典查找:O(1)
  4. 字典的基本操作

    • 创建:{}dict()
    • 访问:dict[key]dict.get(key)
    • 添加:dict[key] = value
    • 修改:dict[key] = new_value
    • 删除:del dict[key]dict.pop(key)
  5. 字典的常用方法

    • keys(): 获取所有键
    • values(): 获取所有值
    • items(): 获取所有键值对
    • get(): 安全访问
    • update(): 更新字典
    • clear(): 清空字典
  6. 遍历字典

    • 遍历键:for key in dict
    • 遍历值:for value in dict.values()
    • 遍历键值对:for key, value in dict.items()
  7. 嵌套字典

    • 字典的值可以是另一个字典
    • 访问嵌套数据:dict[key1][key2]

重要概念

  • 字典的键必须是不可变类型(字符串、数字、元组)
  • 字典的键必须唯一(就像数学函数中每个x只能对应一个y)
  • 字典的查找速度很快(O(1)),因为使用了哈希表
  • 字典适合存储有关联关系的数据

数学知识回顾 📐

通过本章,你复习了以下数学知识:

  1. 映射和函数

    • 理解“输入→输出“的对应关系
    • 一一对应的概念
  2. 统计数据分析

    • 平均数、最大值、最小值
    • 数据分组统计
  3. 单位换算

    • 长度、重量、温度换算
    • 理解比例关系
  4. 找规律

    • 观察数列的变化趋势
    • 发现函数关系式

计算机知识 💻

通过本章,你了解了以下计算机知识:

  1. 哈希表

    • 什么是哈希函数
    • 为什么字典查找速度快
    • 时间复杂度:O(1) vs O(n)
  2. 数据结构选择

    • 什么时候用列表
    • 什么时候用字典
    • 权衡速度和内存

挑战练习

挑战1:制作一个简单的游戏配置系统

创建一个游戏配置系统,可以设置和读取游戏参数(如难度、音量、玩家名称等)。

提示

使用字典存储配置信息,每个配置项是一个键值对。

挑战2:制作一个图书管理系统

创建一个图书管理系统,每本书包含:书名、作者、出版社、出版年份、价格等信息。支持添加、查找、删除、列出所有书籍等功能。

提示

使用嵌套字典,书名作为键,书的详细信息作为值。

挑战3:制作一个简单的投票统计系统

创建一个投票统计系统,可以统计多个候选人的得票数,并按票数排序显示结果。

提示

使用字典存储候选人姓名和票数,使用sorted()函数排序。


下一步

恭喜你完成了字典的学习!现在你已经掌握了:

✅ 列表(List)- 按顺序存储数据 ✅ 字典(Dictionary)- 按键值对存储数据

这两种数据结构是Python中最常用的数据存储方式。你已经理解了:

  • 字典就像数学中的“映射“关系
  • 字典使用哈希表技术,查找速度非常快
  • 字典非常适合存储有关联关系的数据

本章与数学的联结 📊

通过字典,你其实已经在用编程的方式理解数学中的函数概念了!

在数学课上,你学过函数 y = f(x),这和字典的 “键→值” 是不是非常相似?

数学概念Python字典
自变量 x键(key)
因变量 y值(value)
函数 f(x)字典的映射
定义域所有的键
值域所有的值

下一章预告 🚀

下一章,我们将学习函数(Function)。

等等,函数不是数学里的概念吗?为什么编程里也有函数?

非常好的问题!编程中的函数和数学中的函数确实有很多相似之处:

  • 它们都接收“输入“(参数)
  • 它们都进行某种“处理“或“计算“
  • 它们都产生“输出“(返回值)

但编程中的函数更强大,它可以:

  • 让代码更简洁(不用重复写相同的代码)
  • 让代码更清晰(把复杂问题分解成小问题)
  • 让代码可重用(写一次,用多次)

你将学会:

  • 如何定义自己的函数
  • 函数的参数和返回值(就像数学函数的自变量和因变量)
  • 如何用函数解决实际问题

继续加油,你正在成为一名优秀的程序员!🎉

第12章 函数

引言

想象一下,你在玩一个电子游戏。游戏中有一些特殊的动作,比如“跳跃“、“攻击”、“防守”。每次你需要做这些动作时,只需要按一个对应的按钮,游戏角色就会执行这个动作。你不需要每次都重新告诉电脑“抬起左腿、向前移动、放下左腿…“——所有这些复杂的步骤都已经被打包到“跳跃“这个按钮里了。

在Python中,也有这样一种机制,让我们可以把一段代码打包起来,给它起个名字,以后就可以通过这个名字重复使用这段代码。这就是函数(Function)

你已经学过的函数!

其实,你早就在数学课上见过“函数“了!还记得数学里的函数吗?

比如,有一个数学函数:

f(x) = 2x + 1

当我们把x = 3代入这个函数时:

f(3) = 2 × 3 + 1 = 7

当我们把x = 5代入时:

f(5) = 2 × 5 + 1 = 11

你看,数学函数就是这么一个“规则“或“公式“,你给它一个输入值(x),它就按照固定的规则计算,给你一个输出值(结果)。

Python函数和数学函数非常相似!

  • 数学函数:接收一个数,按公式计算,返回结果
  • Python函数:接收一些数据,执行代码,返回结果

它们都有:

  1. 一个名字(比如f、g、draw_square)
  2. 输入(参数)
  3. 处理过程(计算或代码)
  4. 输出(返回值)

给家长的小贴士:

  • 如果孩子已经学过数学函数,可以用数学函数作为类比,这样孩子会更容易理解Python函数的概念
  • 如果孩子还没学过数学函数,可以用“遥控器按钮“、“快捷指令”、“预设程序“等生活中的例子来类比函数
  • 重点强调“输入→处理→输出“这个模式,这是所有函数的核心特征

为什么需要函数

问题:重复的代码

在学习循环语句时,我们用turtle画过很多图形。现在让我们画一个正方形:

import turtle

t = turtle.Pen()

# 画一个正方形
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)

turtle.done()

运行这段代码,会在屏幕上画出一个边长为100的正方形。

现在,如果我们要画两个正方形呢?

import turtle

t = turtle.Pen()

# 画第一个正方形
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)

# 移动到新位置
t.penup()
t.goto(150, 0)
t.pendown()

# 画第二个正方形
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)
t.forward(100)
t.left(90)

turtle.done()

你注意到什么问题了吗?画正方形的代码重复了两次!如果我们要画10个正方形,难道要复制粘贴10次吗?

解决方案:使用函数

函数可以帮我们解决这个问题。我们可以把“画正方形“的代码打包成一个函数:

import turtle

t = turtle.Pen()

# 定义一个画正方形的函数
def draw_square():
    t.forward(100)
    t.left(90)
    t.forward(100)
    t.left(90)
    t.forward(100)
    t.left(90)
    t.forward(100)
    t.left(90)

# 使用函数画第一个正方形
draw_square()

# 移动到新位置
t.penup()
t.goto(150, 0)
t.pendown()

# 使用函数画第二个正方形
draw_square()

turtle.done()

现在代码变得简洁多了!我们只需要调用draw_square()函数,就可以画出一个正方形。

给家长的小贴士:强调函数的两个主要好处:1)避免重复代码;2)让代码更易读、易维护。可以告诉孩子,如果以后想修改正方形的画法,只需要修改函数定义,而不需要修改每一处调用的地方。


函数的基本概念

什么是函数

函数是一个可以重复使用的代码块,它有一个名字,当我们需要使用这段代码时,只需要调用这个名字。

数学函数 vs Python函数

让我们对比一下数学函数和Python函数:

数学函数示例:

f(x) = x²      (平方函数)
g(x, y) = x + y  (加法函数)

对应的Python函数:

def f(x):
    return x * x

def g(x, y):
    return x + y

你看,它们的结构几乎一样!

数学函数Python函数说明
f(x) = x²def f(x): return x * x函数名是f,参数是x
g(a, b) = a + bdef g(a, b): return a + b函数名是g,有两个参数a和b
y = f(3)y = f(3)调用函数,传入参数3

定义函数的语法

def 函数名(参数1, 参数2, ...):
    函数体(要执行的代码)
    return 返回值

让我们分解这个语法:

  • def:这是Python的关键字,告诉计算机“我要定义一个函数了“(就像数学课上说“设f(x) = …“一样)
  • 函数名:给函数起的名字,命名规则和变量名一样(数学里常用f、g、h,Python里常用有意义的名字)
  • (参数1, 参数2, ...):圆括号,里面可以放参数(就像数学函数f(x)中的x)
  • ::冒号,表示函数定义的开始
  • 函数体:缩进的代码块,是函数要执行的内容(就像数学公式中的计算规则)
  • return:返回计算结果(就像数学函数计算出结果)

从数学函数到Python函数

让我们把一些常见的数学函数转换成Python代码:

示例1:绝对值函数

数学中的绝对值函数:

f(x) = |x|

Python实现:

def absolute_value(x):
    """计算x的绝对值(对应数学函数 f(x) = |x|)"""
    if x >= 0:
        return x
    else:
        return -x

# 测试
print(absolute_value(5))    # 输出: 5
print(absolute_value(-5))   # 输出: 5
print(absolute_value(0))    # 输出: 0

示例2:简单线性函数

数学函数:

f(x) = 2x + 3

Python实现:

def linear_function(x):
    """计算2x + 3(线性函数)"""
    return 2 * x + 3

# 测试
print(linear_function(1))    # 输出: 5 (2×1+3=5)
print(linear_function(5))    # 输出: 13 (2×5+3=13)
print(linear_function(0))    # 输出: 3 (2×0+3=3)

给家长的数学提示:

  • 绝对值函数| x |是小学高年级数学的内容,表示一个数到0的距离
  • 线性函数 f(x) = ax + b 的形式也很适合作为入门例子
  • 可以鼓励孩子用Python验证他们学过的数学计算

一个简单的例子

# 定义一个打招呼的函数
def say_hello():
    print("你好!")
    print("欢迎来到Python世界!")
    print("让我们一起学习编程吧!")

# 调用函数
say_hello()

print("-----分隔线-----")

# 再次调用函数
say_hello()

输出:

你好!
欢迎来到Python世界!
让我们一起学习编程吧!
-----分隔线-----
你好!
欢迎来到Python世界!
让我们一起学习编程吧!

给家长的小贴士:解释函数的“定义“和“调用“是两个不同的步骤。定义函数就像是在编写一本“操作手册“或数学公式,而调用函数就像是按照手册执行操作或代入数值计算。定义函数时,代码不会立即执行,只有调用时才会执行。


函数的参数

问题:函数太死板

现在我们有了一个画正方形的函数:

import turtle

t = turtle.Pen()

def draw_square():
    t.forward(100)
    t.left(90)
    t.forward(100)
    t.left(90)
    t.forward(100)
    t.left(90)
    t.forward(100)
    t.left(90)

# 在不同位置画正方形
draw_square()

t.penup()
t.goto(150, 0)
t.pendown()

draw_square()

turtle.done()

这个函数有一个问题:它只能画边长为100的正方形。如果我们想画不同大小的正方形怎么办?

解决方案:带参数的函数

我们可以给函数添加参数,让函数更灵活:

import turtle

t = turtle.Pen()

def draw_square(size):
    t.forward(size)
    t.left(90)
    t.forward(size)
    t.left(90)
    t.forward(size)
    t.left(90)
    t.forward(size)
    t.left(90)

# 画一个边长为50的正方形
draw_square(50)

# 移动位置
t.penup()
t.goto(100, 0)
t.pendown()

# 画一个边长为100的正方形
draw_square(100)

# 移动位置
t.penup()
t.goto(250, 0)
t.pendown()

# 画一个边长为150的正方形
draw_square(150)

turtle.done()

现在draw_square()函数可以根据传入的参数画出不同大小的正方形了!

什么是参数

参数就像是函数的“输入“,让我们可以向函数传递信息。

数学函数的参数:

f(x) = 2x + 1
     ↑
     参数(自变量)

当我们说f(5)时,5就是传入参数的值。

Python函数的参数:

def double(x):     # x是参数(形参)
    return 2 * x

result = double(5)  # 5是实参
  • 参数(Parameter/形参):函数定义时括号里的变量名(就像数学函数中的x)
  • 实参(Argument):调用函数时传入的实际值(就像代入x的具体数值)
def draw_square(size):  # size是参数(形参)
    t.forward(size)
    t.left(90)
    ...

draw_square(100)  # 100是实参

数学练习:理解参数

让我们通过数学练习来理解参数的概念:

练习1:理解函数调用

数学函数: f(x) = x²

如果我们计算:

  • f(2) = 4
  • f(5) = 25
  • f(10) = 100

这里2、5、10都是“实参“,x是“参数“(形参)

对应的Python代码:

def square(x):  # x是参数
    return x * x

print(square(2))    # 输出: 4
print(square(5))    # 输出: 25
print(square(10))   # 输出: 100

练习2:多参数函数

数学函数: f(x, y) = x + y

计算:

  • f(3, 5) = 8
  • f(10, 20) = 30

对应的Python代码:

def add(x, y):
    return x + y

print(add(3, 5))     # 输出: 8
print(add(10, 20))   # 输出: 30

多个参数

函数可以有多个参数,用逗号分隔:

import turtle

t = turtle.Pen()

def draw_rectangle(width, height):
    """画一个矩形"""
    t.forward(width)
    t.left(90)
    t.forward(height)
    t.left(90)
    t.forward(width)
    t.left(90)
    t.forward(height)
    t.left(90)

# 画不同大小的矩形
draw_rectangle(100, 50)

t.penup()
t.goto(150, 0)
t.pendown()

draw_rectangle(50, 100)

turtle.done()

位置参数

当我们调用函数时,实参会按照位置对应到参数:

def greet(name, age):
    print(f"你好,我叫{name},今年{age}岁")

# 第一个值对应name,第二个值对应age
greet("小明", 12)  # 输出: 你好,我叫小明,今年12岁

# 如果顺序错了,结果也会错
greet(12, "小明")  # 输出: 你好,我叫12,今年小明岁(错了!)

给家长的小贴士:可以用“快递包裹“来类比参数。快递单上有“寄件人“、“收件人”、“地址“等字段(参数),填写快递单时需要把具体信息(实参)填入对应的字段。

练习:为函数添加颜色参数

我们可以让画正方形的函数支持颜色选择:

import turtle

t = turtle.Pen()

def draw_colored_square(size, color):
    """画一个指定大小和颜色的正方形"""
    t.color(color)  # 设置颜色
    t.begin_fill()  # 开始填充
    t.forward(size)
    t.left(90)
    t.forward(size)
    t.left(90)
    t.forward(size)
    t.left(90)
    t.forward(size)
    t.left(90)
    t.end_fill()  # 结束填充

# 画不同颜色的正方形
draw_colored_square(100, "red")

t.penup()
t.goto(120, 0)
t.pendown()

draw_colored_square(100, "blue")

t.penup()
t.goto(240, 0)
t.pendown()

draw_colored_square(100, "green")

turtle.done()

练习:画一排正方形

结合循环,我们可以画一排正方形:

import turtle

t = turtle.Pen()
t.speed(0)  # 设置最快速度

def draw_square(size, color):
    """画一个正方形"""
    t.color(color)
    t.begin_fill()
    for i in range(4):
        t.forward(size)
        t.left(90)
    t.end_fill()

# 画一排正方形
colors = ["red", "orange", "yellow", "green", "blue"]
for i in range(5):
    draw_square(50, colors[i])
    t.penup()
    t.forward(70)
    t.pendown()

turtle.done()

默认参数

什么需要默认参数

有时候,我们希望函数的某些参数有默认值。这样,调用函数时如果不提供这个参数,就会使用默认值。

import turtle

t = turtle.Pen()

def draw_square(size, color="red"):
    """画正方形,默认颜色是红色"""
    t.color(color)
    t.begin_fill()
    for i in range(4):
        t.forward(size)
        t.left(90)
    t.end_fill()

# 不指定颜色,使用默认的红色
draw_square(100)

t.penup()
t.goto(120, 0)
t.pendown()

# 指定颜色为蓝色
draw_square(100, "blue")

turtle.done()

默认参数的规则

  1. 默认参数必须在非默认参数之后
  2. 调用函数时,可以只给部分参数赋值
# 错误的例子
def draw_square(color="red", size):  # 错误!默认参数在后面
    ...

# 正确的例子
def draw_square(size, color="red"):  # 正确!
    ...

# 调用时可以只给第一个参数
draw_square(100)  # color使用默认值"red"

给家长的小贴士:可以用“订购商品“来类比默认参数。比如订购pizza时,默认配料是芝士,但你可以选择添加其他配料。


函数的返回值

什么是返回值

数学函数的返回值:

f(x) = x²
f(3) = 9  ← 9就是返回值(因变量)

在数学中,我们把函数计算的结果叫做“函数值“或“因变量“。

Python函数的返回值:

到目前为止,我们的函数都是执行某些操作(画图、打印等)。但有时,我们希望函数能够“返回“一个结果,让我们可以在程序中使用这个结果。这就像数学函数计算出一个数值一样。

# 计算矩形的面积(对应数学函数 A = w × h)
def calculate_area(width, height):
    area = width * height
    return area  # 返回计算结果,就像数学函数得出答案

# 调用函数并获取返回值
result = calculate_area(10, 5)
print(f"矩形的面积是: {result}")

return语句:数学函数的“=“号

在数学函数中,我们用“=“号表示结果:

f(x) = 2x + 1
f(3) = 7  ← 计算结果是7

在Python中,我们用return语句返回结果:

def f(x):
    return 2 * x + 1  # 返回计算结果

result = f(3)  # result的值是7

可以把return理解为数学中的“等于“——它告诉Python:“这个函数的值是xxx”。

从数学公式到Python函数

让我们把一些常见的数学公式转换成Python函数:

示例1:矩形面积公式

数学公式: A = 长 × 宽

def rectangle_area(length, width):
    """计算矩形面积 A = l × w"""
    return length * width

# 测试
print(rectangle_area(10, 5))  # 输出: 50
print(rectangle_area(8, 3))   # 输出: 24

示例2:矩形周长公式

数学公式: P = 2 × (长 + 宽)

def rectangle_perimeter(length, width):
    """计算矩形周长 P = 2(l + w)"""
    return 2 * (length + width)

# 测试
print(rectangle_perimeter(10, 5))  # 输出: 30
print(rectangle_perimeter(8, 3))   # 输出: 22

示例3:三角形面积公式(海伦公式)

数学公式: A = √[s(s-a)(s-b)(s-c)] 其中 s = (a+b+c)/2

import math

def triangle_area(a, b, c):
    """用海伦公式计算三角形面积"""
    s = (a + b + c) / 2  # 半周长
    area = math.sqrt(s * (s - a) * (s - b) * (s - c))
    return area

# 测试:边长为3, 4, 5的三角形(直角三角形)
print(triangle_area(3, 4, 5))  # 输出: 6.0 (应该是6,因为3×4÷2=6)

示例4:圆的面积和周长

数学公式:

  • 面积: A = πr²
  • 周长: C = 2πr
import math

def circle_area(radius):
    """计算圆的面积 A = πr²"""
    return math.pi * radius * radius

def circle_circumference(radius):
    """计算圆的周长 C = 2πr"""
    return 2 * math.pi * radius

# 测试:半径为5的圆
r = 5
print(f"半径为{r}的圆:")
print(f"  面积: {circle_area(r):.2f}")
print(f"  周长: {circle_circumference(r):.2f}")

给家长的数学提示:

  • 这些公式都是小学高年级或初中数学的内容
  • 可以让孩子先用数学方法手工计算,再用Python验证
  • 海伦公式可能比较难,可以根据孩子的情况决定是否讲解
  • 圆周率π用math.pi表示,这是Python数学库提供的精确值

返回值的使用

返回值可以像普通变量一样使用:

def calculate_rectangle_area(width, height):
    return width * height

def calculate_circle_area(radius):
    return 3.14 * radius * radius

# 比较两个面积
rectangle = calculate_rectangle_area(10, 5)
circle = calculate_circle_area(5)

print(f"矩形面积: {rectangle}")
print(f"圆形面积: {circle:.2f}")

if rectangle > circle:
    print("矩形的面积更大")
else:
    print("圆形的面积更大")

多个返回值

函数可以返回多个值:

def calculate(width, height):
    """计算矩形的面积和周长"""
    area = width * height
    perimeter = 2 * (width + height)
    return area, perimeter

# 接收多个返回值
a, p = calculate(10, 5)
print(f"面积: {a}, 周长: {p}")

给家长的小贴士:可以用“自动售货机“来类比返回值。你投入硬币(参数),按下按钮(调用函数),机器会“返回“商品(返回值)。

练习:温度转换函数

def celsius_to_fahrenheit(celsius):
    """将摄氏度转换为华氏度"""
    fahrenheit = celsius * 9/5 + 32
    return fahrenheit

def fahrenheit_to_celsius(fahrenheit):
    """将华氏度转换为摄氏度"""
    celsius = (fahrenheit - 32) * 5/9
    return celsius

# 测试函数
temp_c = 25
temp_f = celsius_to_fahrenheit(temp_c)
print(f"{temp_c}°C = {temp_f}°F")

temp_f = 77
temp_c = fahrenheit_to_celsius(temp_f)
print(f"{temp_f}°F = {temp_c:.1f}°C")

变量的作用域

局部变量

在函数内部定义的变量叫做局部变量,它们只能在函数内部使用:

def my_function():
    local_var = 10  # 局部变量
    print(f"函数内部: local_var = {local_var}")

my_function()  # 输出: 函数内部: local_var = 10

# 下面这行会报错!
# print(local_var)  # NameError: name 'local_var' is not defined

全局变量

在函数外部定义的变量叫做全局变量,它们可以在整个程序中使用:

global_var = 100  # 全局变量

def my_function():
    print(f"函数内部可以访问: global_var = {global_var}")

my_function()  # 输出: 函数内部可以访问: global_var = 100
print(f"函数外部也可以访问: global_var = {global_var}")

全局变量和局部变量的同名

如果全局变量和局部变量同名,局部变量会“遮蔽“全局变量:

x = 10  # 全局变量

def my_function():
    x = 20  # 局部变量,不影响全局变量
    print(f"函数内部: x = {x}")

my_function()  # 输出: 函数内部: x = 20
print(f"函数外部: x = {x}")  # 输出: 函数外部: x = 10

在函数中修改全局变量

如果想在函数中修改全局变量,需要使用global关键字:

x = 10  # 全局变量

def modify_global():
    global x  # 声明要使用全局变量x
    x = 20
    print(f"函数内部修改后: x = {x}")

print(f"修改前: x = {x}")  # 输出: 修改前: x = 10
modify_global()  # 输出: 函数内部修改后: x = 20
print(f"修改后: x = {x}")  # 输出: 修改后: x = 20

给家长的小贴士:作用域是一个比较抽象的概念。可以用“家里的房间“来类比:每个房间(函数)都有自己的私人物品(局部变量),但客厅(全局区域)的物品大家都可以用。


函数的文档字符串

什么是文档字符串

文档字符串(Docstring)是用来说明函数功能的注释,放在函数定义的第一行:

def calculate_area(width, height):
    """
    计算矩形的面积

    参数:
        width: 矩形的宽度
        height: 矩形的高度

    返回:
        矩形的面积
    """
    return width * height

查看文档字符串

可以使用help()函数查看文档字符串:

def greet(name):
    """向指定的人打招呼"""
    print(f"你好, {name}!")

# 查看文档字符串
help(greet)

输出:

Help on function greet in module __main__:

greet(name)
    向指定的人打招呼

给家长的小贴士:教导孩子养成写文档字符串的好习惯,这就像给函数写“使用说明书“,以后自己或别人使用这个函数时,就能快速了解它的功能。


函数的高级应用

画花朵图案

让我们用函数画一个美丽的花朵图案:

import turtle

t = turtle.Pen()
t.speed(0)

def draw_petal(size, angle):
    """画一个花瓣"""
    for i in range(2):
        t.circle(size, angle)
        t.left(180 - angle)

def draw_flower(x, y, size, petal_count, color):
    """在指定位置画一朵花"""
    t.penup()
    t.goto(x, y)
    t.pendown()
    t.color(color)
    t.begin_fill()

    # 画花瓣
    for i in range(petal_count):
        draw_petal(size, 60)
        t.left(360 / petal_count)

    t.end_fill()

# 画不同的花朵
draw_flower(-100, 100, 50, 6, "red")
draw_flower(100, 100, 40, 8, "yellow")
draw_flower(0, -100, 60, 10, "purple")

turtle.done()

画N角星

编写一个函数,可以画任意角数的星:

import turtle

t = turtle.Pen()
t.speed(0)

def draw_star(x, y, size, points, color, angle=None):
    """
    画一个N角星

    参数:
        x, y: 位置
        size: 大小
        points: 角的数量
        color: 颜色
        angle: 旋转角度(可选)
    """
    t.penup()
    t.goto(x, y)
    t.pendown()

    if angle is not None:
        t.right(angle)

    t.color(color)
    t.begin_fill()

    # 计算角度
    if points == 5:
        exterior_angle = 144
    else:
        exterior_angle = 180 - 180 / points

    for i in range(points):
        t.forward(size)
        t.right(exterior_angle)

    t.end_fill()

# 画不同的星星
draw_star(-100, 100, 100, 5, "yellow", 0)     # 五角星
draw_star(100, 100, 80, 6, "red", 30)        # 六角星
draw_star(0, -100, 120, 8, "blue", 45)      # 八角星

turtle.done()

递归函数

什么是递归

递归是指函数调用自身。听起来很奇怪,但这是一个非常强大的编程技巧。

递归就像数学归纳法

如果你学过数学归纳法,递归的概念就很相似了:

数学归纳法证明:

  1. 基准情况:证明当n=1时命题成立
  2. 归纳假设:假设当n=k时命题成立
  3. 归纳递推:证明当n=k+1时命题也成立

递归函数:

  1. 基准情况:最简单的情况,直接返回结果
  2. 递归调用:调用自身解决更小的问题
  3. 递归推进:每次调用要朝着基准情况前进

递归的要素

编写递归函数时,必须注意以下几点:

  1. 必须有退出条件(基准情况)
  2. 每次调用时,参数要发生变化
  3. 参数变化要朝着退出条件的方向进行

递归示例1:阶乘

数学定义:

n! = n × (n-1) × (n-2) × ... × 2 × 1

递归定义:

n! = n × (n-1)!      (当n > 1时)
1! = 1               (基准情况)

比如:

  • 5! = 5 × 4!
  • 4! = 4 × 3!
  • 3! = 3 × 2!
  • 2! = 2 × 1!
  • 1! = 1 (基准情况)

Python实现:

def factorial(n):
    """计算n的阶乘 n!"""
    # 退出条件(基准情况)
    if n == 1:
        return 1

    # 递归调用
    return n * factorial(n - 1)

# 测试
print(factorial(5))  # 输出: 120
print(factorial(4))  # 输出: 24
print(factorial(1))  # 输出: 1

让我们追踪一下factorial(5)的执行过程:

factorial(5)
= 5 * factorial(4)
= 5 * (4 * factorial(3))
= 5 * (4 * (3 * factorial(2)))
= 5 * (4 * (3 * (2 * factorial(1))))
= 5 * (4 * (3 * (2 * 1)))       ← 到达基准情况!
= 5 * (4 * (3 * 2))
= 5 * (4 * 6)
= 5 * 24
= 120

递归示例2:求和

数学定义: 计算1到n的和:

S(n) = 1 + 2 + 3 + ... + n

递归定义:

S(n) = n + S(n-1)    (当n > 1时)
S(1) = 1             (基准情况)

Python实现:

def sum_to_n(n):
    """递归计算1到n的和"""
    # 退出条件
    if n == 1:
        return 1

    # 递归调用
    return n + sum_to_n(n - 1)

# 测试
print(sum_to_n(5))   # 输出: 15 (1+2+3+4+5)
print(sum_to_n(10))  # 输出: 55

数学验证: 可以用等差数列求和公式验证:

S(n) = n(n+1)/2
S(10) = 10×11/2 = 55 ✓

Python验证:

def sum_formula(n):
    """用公式计算1到n的和: S = n(n+1)/2"""
    return n * (n + 1) // 2

print(sum_formula(10))  # 输出: 55

递归示例3:斐波那契数列

数学背景:

斐波那契数列是一个著名的数学数列,它在自然界中随处可见(比如花朵的花瓣数、蜗牛的壳纹等)。

数列是这样的:

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...

递归定义:

F(n) = F(n-1) + F(n-2)   (当n > 2时)
F(1) = 1                  (基准情况)
F(2) = 1                  (基准情况)

规律是:每个数都是前两个数之和

比如:

  • F(3) = F(2) + F(1) = 1 + 1 = 2
  • F(4) = F(3) + F(2) = 2 + 1 = 3
  • F(5) = F(4) + F(3) = 3 + 2 = 5

Python实现:

def fibonacci(n):
    """计算斐波那契数列的第n项"""
    # 退出条件
    if n == 1 or n == 2:
        return 1

    # 递归调用
    return fibonacci(n - 1) + fibonacci(n - 2)

# 打印前10项
for i in range(1, 11):
    print(fibonacci(i), end=" ")
# 输出: 1 1 2 3 5 8 13 21 34 55

数学小知识:

  • 斐波那契数列的相邻两项比值会越来越接近“黄金比例“φ ≈ 1.618
  • F(n+1)/F(n) ≈ 1.618 (当n很大时)

让我们验证一下:

def fibonacci(n):
    if n == 1 or n == 2:
        return 1
    return fibonacci(n - 1) + fibonacci(n - 2)

# 计算比值
for i in range(5, 16):
    ratio = fibonacci(i + 1) / fibonacci(i)
    print(f"F({i+1})/F({i}) = {ratio:.6f}")

递归示例:画分形树

import turtle

t = turtle.Pen()
t.speed(0)
t.left(90)

def draw_tree(branch_length, pen_size):
    """画分形树"""
    if branch_length < 10:
        return

    # 设置画笔粗细
    t.pensize(pen_size)

    # 画树干
    t.forward(branch_length)

    # 画右边的树枝
    t.right(20)
    draw_tree(branch_length * 0.7, pen_size * 0.7)

    # 画左边的树枝
    t.left(40)
    draw_tree(branch_length * 0.7, pen_size * 0.7)

    # 回到树干
    t.right(20)
    t.penup()
    t.backward(branch_length)
    t.pendown()

# 画树
draw_tree(100, 10)

turtle.done()

给家长的小贴士:递归是一个比较难理解的概念。可以用“俄罗斯套娃“或“照镜子“来类比。提醒孩子,递归必须有退出条件,否则会陷入无限循环(就像程序里跑进了一个永远出不去的迷宫)。如果孩子学过数学归纳法,可以用数学归纳法来类比递归的思想。


数学函数练习

让我们通过一些数学练习来巩固函数的概念!

练习1:最大值函数

编写一个函数,找出两个数中的最大值。

数学定义:

max(a, b) = a   (如果 a ≥ b)
max(a, b) = b   (如果 a < b)
查看答案
def maximum(a, b):
    """返回两个数中的最大值"""
    if a >= b:
        return a
    else:
        return b

# 测试
print(maximum(5, 3))   # 输出: 5
print(maximum(2, 8))   # 输出: 8
print(maximum(4, 4))   # 输出: 4

练习2:最小公倍数(LCM)

编写一个函数,计算两个数的最小公倍数。

数学背景: 最小公倍数是能够被这两个数整除的最小的正整数。

比如:

  • LCM(4, 6) = 12 (12能被4和6整除)
  • LCM(3, 5) = 15 (15能被3和5整除)

公式:

LCM(a, b) = (a × b) / GCD(a, b)

其中GCD是最大公约数。

查看答案
def gcd(a, b):
    """计算最大公约数(欧几里得算法)"""
    while b != 0:
        a, b = b, a % b
    return a

def lcm(a, b):
    """计算最小公倍数"""
    return (a * b) // gcd(a, b)

# 测试
print(f"LCM(4, 6) = {lcm(4, 6)}")    # 输出: 12
print(f"LCM(3, 5) = {lcm(3, 5)}")    # 输出: 15
print(f"LCM(12, 18) = {lcm(12, 18)}")  # 输出: 36

练习3:判断素数

编写一个函数,判断一个数是否为素数。

数学背景: 素数是只能被1和它本身整除的大于1的正整数。

比如:2, 3, 5, 7, 11, 13, 17, 19, …

查看答案
def is_prime(n):
    """判断n是否为素数"""
    if n < 2:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False

    # 只需要检查到√n
    i = 3
    while i * i <= n:
        if n % i == 0:
            return False
        i += 2
    return True

# 测试
for i in range(1, 21):
    if is_prime(i):
        print(i, end=" ")
# 输出: 2 3 5 7 11 13 17 19

给家长的数学提示:

  • 最大公约数可以用欧几里得算法计算,这是一个经典的算法
  • 判断素数时,只需要检查到√n,因为如果n有因数,必然有一个≤√n
  • 这些练习可以帮孩子复习数学概念,同时学习Python编程

练习4:摄氏度和华氏度转换

编写两个函数,实现温度单位转换。

数学公式:

  • 摄氏度转华氏度: F = C × 9/5 + 32
  • 华氏度转摄氏度: C = (F - 32) × 5/9
查看答案
def celsius_to_fahrenheit(c):
    """摄氏度转华氏度: F = C × 9/5 + 32"""
    return c * 9/5 + 32

def fahrenheit_to_celsius(f):
    """华氏度转摄氏度: C = (F - 32) × 5/9"""
    return (f - 32) * 5/9

# 测试
print(f"25°C = {celsius_to_fahrenheit(25):.1f}°F")  # 输出: 77.0°F
print(f"77°F = {fahrenheit_to_celsius(77):.1f}°C")  # 输出: 25.0°C

# 常见温度对比
print(f"\n水的冰点: 0°C = {celsius_to_fahrenheit(0)}°F")
print(f"水的沸点: 100°C = {celsius_to_fahrenheit(100)}°F")
print(f"人体体温: 37°C = {celsius_to_fahrenheit(37):.1f}°F")

练习5:计算平均数

编写一个函数,计算列表中所有数的平均数。

数学公式:

平均值 = (所有数的和) / (数的个数)
查看答案
def calculate_average(numbers):
    """计算平均数"""
    if len(numbers) == 0:
        return 0
    return sum(numbers) / len(numbers)

# 测试
scores = [85, 92, 78, 95, 88]
average = calculate_average(scores)
print(f"平均分: {average:.1f}")

# 另一个例子
heights = [1.65, 1.72, 1.68, 1.75, 1.70]
avg_height = calculate_average(heights)
print(f"平均身高: {avg_height:.2f}米")

练习6:幂函数

编写一个递归函数,计算x的n次方。

数学定义:

xⁿ = x × x × ... × x  (共n个x相乘)

递归定义:

xⁿ = x × xⁿ⁻¹   (当n > 0时)
x⁰ = 1          (基准情况)
查看答案
def power(x, n):
    """计算x的n次方"""
    if n == 0:
        return 1
    return x * power(x, n - 1)

# 测试
print(f"2³ = {power(2, 3)}")    # 输出: 8
print(f"3⁴ = {power(3, 4)}")    # 输出: 81
print(f"5⁰ = {power(5, 0)}")    # 输出: 1

# 验证:用Python的**运算符
print(f"\n验证:")
print(f"2**10 = {power(2, 10)}")  # 输出: 1024
print(f"10**3 = {power(10, 3)}")  # 输出: 1000

实践练习

练习1:基本函数

编写一个函数,接收一个名字作为参数,打印出“你好,[名字]!“

查看答案
def greet(name):
    print(f"你好,{name}!")

greet("小明")
greet("小红")

练习2:带参数的函数

编写一个函数,接收三个参数:宽、高和颜色,画出一个填充的矩形。

查看答案
import turtle

t = turtle.Pen()

def draw_rectangle(width, height, color):
    t.color(color)
    t.begin_fill()
    for i in range(2):
        t.forward(width)
        t.left(90)
        t.forward(height)
        t.left(90)
    t.end_fill()

draw_rectangle(100, 50, "red")

练习3:带返回值的函数

编写一个函数,接收一个数字作为参数,返回这个数字的平方。

查看答案
def square(x):
    return x * x

result = square(5)
print(result)  # 输出: 25

练习4:多个返回值

编写一个函数,接收一个矩形的宽和高,返回矩形的面积和周长。

查看答案
def rectangle_info(width, height):
    area = width * height
    perimeter = 2 * (width + height)
    return area, perimeter

a, p = rectangle_info(10, 5)
print(f"面积: {a}, 周长: {p}")

练习5:默认参数

编写一个函数,画一个正六边形,默认边长为50,默认颜色为蓝色。

查看答案
import turtle

t = turtle.Pen()

def draw_hexagon(size=50, color="blue"):
    t.color(color)
    t.begin_fill()
    for i in range(6):
        t.forward(size)
        t.left(60)
    t.end_fill()

# 使用默认值
draw_hexagon()

t.penup()
t.goto(100, 0)
t.pendown()

# 使用自定义值
draw_hexagon(80, "red")

turtle.done()

练习6:画螺旋图案

编写一个函数,画一个螺旋图案。

查看答案
import turtle

t = turtle.Pen()
t.speed(0)

def draw_spiral(lines, length, angle, increment):
    """画螺旋图案"""
    for i in range(lines):
        t.forward(length + i * increment)
        t.left(angle)

draw_spiral(50, 5, 90, 2)

turtle.done()

练习7:计算器函数

编写一个计算器函数,接收三个参数:第一个数字、操作符、第二个数字,返回计算结果。

查看答案
def calculate(num1, operator, num2):
    if operator == "+":
        return num1 + num2
    elif operator == "-":
        return num1 - num2
    elif operator == "*":
        return num1 * num2
    elif operator == "/":
        if num2 != 0:
            return num1 / num2
        else:
            return "错误:除数不能为0"
    else:
        return "错误:不支持的操作符"

# 测试
print(calculate(10, "+", 5))   # 输出: 15
print(calculate(10, "-", 5))   # 输出: 5
print(calculate(10, "*", 5))   # 输出: 50
print(calculate(10, "/", 5))   # 输出: 2.0

练习8:递归求和

编写一个递归函数,计算从1到n的和。

查看答案
def sum_to_n(n):
    """递归计算1到n的和"""
    # 退出条件
    if n == 1:
        return 1

    # 递归调用
    return n + sum_to_n(n - 1)

print(sum_to_n(5))  # 输出: 15 (1+2+3+4+5)
print(sum_to_n(10))  # 输出: 55

练习9:递归幂运算

编写一个递归函数,计算x的n次方。

查看答案
def power(x, n):
    """递归计算x的n次方"""
    # 退出条件
    if n == 0:
        return 1

    # 递归调用
    return x * power(x, n - 1)

print(power(2, 3))  # 输出: 8 (2*2*2)
print(power(3, 4))  # 输出: 81 (3*3*3*3)

练习10:画多彩图案

编写一个函数,画一个多彩的圆形图案。

查看答案
import turtle

t = turtle.Pen()
t.speed(0)
turtle.bgcolor("black")

colors = ["red", "yellow", "blue", "green", "orange", "purple"]

def draw_colorful_circle(size, colors):
    """画多彩圆形图案"""
    for i in range(len(colors)):
        t.color(colors[i])
        t.begin_fill()
        t.circle(size)
        t.end_fill()
        t.left(360 / len(colors))

draw_colorful_circle(100, colors)

turtle.done()

综合项目:图形绘制系统

结合所学的知识,创建一个图形绘制系统:

import turtle

t = turtle.Pen()
t.speed(0)

def draw_square(size, color, x=0, y=0):
    """画正方形"""
    t.penup()
    t.goto(x, y)
    t.pendown()
    t.color(color)
    t.begin_fill()
    for i in range(4):
        t.forward(size)
        t.left(90)
    t.end_fill()

def draw_triangle(size, color, x=0, y=0):
    """画三角形"""
    t.penup()
    t.goto(x, y)
    t.pendown()
    t.color(color)
    t.begin_fill()
    for i in range(3):
        t.forward(size)
        t.left(120)
    t.end_fill()

def draw_circle(radius, color, x=0, y=0):
    """画圆形"""
    t.penup()
    t.goto(x, y - radius)
    t.pendown()
    t.color(color)
    t.begin_fill()
    t.circle(radius)
    t.end_fill()

def draw_star(size, color, x=0, y=0):
    """画五角星"""
    t.penup()
    t.goto(x, y)
    t.pendown()
    t.color(color)
    t.begin_fill()
    for i in range(5):
        t.forward(size)
        t.right(144)
    t.end_fill()

# 使用函数绘制图案
def draw_house():
    """画房子"""
    # 房子主体
    draw_square(100, "brown", -50, -50)

    # 屋顶
    draw_triangle(120, "red", -60, 50)

    # 门
    draw_square(30, "yellow", -15, -50)

    # 窗户
    draw_square(20, "lightblue", -40, -20)
    draw_square(20, "lightblue", 20, -20)

def draw_flower_garden():
    """画花园"""
    flowers = [
        (-100, -100, "red"),
        (-50, -120, "yellow"),
        (0, -100, "purple"),
        (50, -120, "pink"),
        (100, -100, "orange")
    ]

    for x, y, color in flowers:
        draw_star(20, color, x, y)

def draw_scene():
    """画完整场景"""
    # 画天空
    draw_circle(50, "yellow", -150, 150)  # 太阳

    # 画房子
    draw_house()

    # 画花园
    draw_flower_garden()

# 绘制场景
draw_scene()

turtle.done()

章节小结

核心知识点

  1. 函数的基本概念:

    • 函数是可重复使用的代码块
    • Python函数和数学函数非常相似
    • 都有:名字、参数、处理过程、返回值
    • 定义函数使用def关键字,调用函数使用函数名和括号
  2. 数学函数 vs Python函数:

    • 数学函数: f(x) = x²
    • Python函数: def f(x): return x * x
    • 参数就像数学函数的自变量
    • 返回值就像数学函数的因变量/函数值
  3. 函数的参数:

    • 参数让函数更灵活
    • 可以有多个参数
    • 可以设置默认参数值
    • 实参按位置对应到参数(像代入数学函数的数值)
  4. 函数的返回值:

    • 使用return语句返回值(类似数学函数计算结果)
    • 返回值可以在程序中使用
    • 可以返回多个值
    • 可以把数学公式转换成Python函数
  5. 变量的作用域:

    • 局部变量:函数内部定义,只在函数内有效
    • 全局变量:函数外部定义,在整个程序有效
    • 使用global关键字修改全局变量
  6. 递归函数:

    • 函数调用自身(类似数学归纳法)
    • 必须有退出条件(基准情况)
    • 每次调用参数要变化
    • 常见应用:阶乘、斐波那契数列、求和
  7. 文档字符串:

    • 用来说明函数功能
    • 使用help()查看

数学知识回顾

本章涉及的重要数学概念:

  • 函数概念:输入→处理→输出
  • 绝对值: |x|
  • 线性函数: f(x) = ax + b
  • 几何公式:矩形面积/周长、圆面积/周长、三角形面积
  • 阶乘: n! = n × (n-1) × … × 1
  • 等差数列求和: S(n) = n(n+1)/2
  • 斐波那契数列: F(n) = F(n-1) + F(n-2)
  • 最大公约数和最小公倍数
  • 素数判断
  • 温度转换公式

重要概念

  • 函数提高了代码的复用性
  • 函数让代码更易读、易维护
  • 参数是函数的输入(像数学函数的自变量)
  • 返回值是函数的输出(像数学函数的因变量)
  • 递归必须有退出条件(基准情况)
  • Python函数是数学函数在编程中的实现
  • 可以用Python验证和探索数学概念

常见错误和调试

错误1:忘记定义函数

# 错误:直接调用未定义的函数
draw_square()

# 正确:先定义,再调用
def draw_square():
    ...

draw_square()

错误2:参数数量不匹配

def greet(name, age):
    print(f"我叫{name},{age}岁")

# 错误:参数数量不对
greet("小明")  # TypeError: greet() missing 1 required positional argument

# 正确:提供所有参数
greet("小明", 12)

错误3:混淆定义和调用

# 错误:忘记括号
def greet():
    print("你好")

greet  # 这只是函数名,不会调用

# 正确:使用括号调用
greet()  # 这才是调用函数

错误4:递归没有退出条件

# 错误:没有退出条件,会导致无限递归
def count_down(n):
    print(n)
    count_down(n - 1)

count_down(5)  # 最终会报错:RecursionError

# 正确:添加退出条件
def count_down(n):
    print(n)
    if n > 1:  # 退出条件
        count_down(n - 1)

调试技巧

  1. 打印调试:
def my_function(x):
    print(f"函数收到参数: {x}")  # 检查参数
    result = x * 2
    print(f"函数计算结果: {result}")  # 检查结果
    return result
  1. 检查返回值:
result = my_function(5)
print(f"返回值是: {result}")
  1. 使用type()检查类型:
print(type(result))  # 确认返回值类型

给家长的小贴士:函数的错误通常比较隐蔽,建议在调试时多使用print()语句,逐步检查函数的执行过程。


挑战练习

挑战1:改进画图函数

改进前面的图形绘制系统,添加更多图形(如菱形、六边形等),并让函数支持边框颜色和填充颜色分开设置。

提示

可以添加参数,如fill_colorborder_color

挑战2:编写成绩等级函数

编写一个函数,接收分数作为参数,返回等级(A/B/C/D/F)。

提示

使用条件语句判断分数范围。

挑战3:递归画图

编写一个递归函数,画一个更复杂的分形图案(如谢尔宾斯基三角形)。

提示

可以画三个小三角形,每个在大三角形的顶点处。


下一步

恭喜你完成了函数的学习!现在你已经掌握了:

✅ 变量和数据类型 ✅ 条件语句和循环语句 ✅ 列表和字典 ✅ 函数和递归用Python实现数学函数

数学与编程的联系

通过这一章,你发现了Python函数和数学函数有很多相似之处:

  1. 函数结构:

    • 数学: f(x) = 2x + 1
    • Python: def f(x): return 2*x + 1
  2. 参数和返回值:

    • 参数就像数学函数的自变量
    • 返回值就像数学函数的因变量
  3. 函数的应用:

    • 数学:描述数量关系和变化规律
    • Python:实现计算逻辑和数据处理

用Python学习数学

现在你可以用Python来:

  • 验证数学计算
  • 探索数学规律(比如斐波那契数列)
  • 实现数学公式
  • 可视化数学概念

继续探索:

  • 尝试用Python实现更多数学公式
  • 探索数列、函数图像等数学概念
  • 用编程解决数学问题

这些是Python编程的核心基础知识。下一章,我们将学习库(Library),这将让我们的程序变得更加强大。

库就像是Python的“工具箱“,里面装满了别人已经写好的功能,我们可以直接使用,而不需要自己从头开始写。比如:

  • math库:提供各种数学函数(三角函数、对数等)
  • random库:生成随机数
  • turtle库:画图

这将大大扩展我们的编程能力!

继续加油,你正在成为一名优秀的程序员!🎉

库 - 编程的百宝箱 🎁

引言

想象一下,如果你玩乐高积木时,每个零件都要自己亲手制造,那该多累啊!幸运的是,乐高公司已经为你准备好了成千上万种积木零件:车轮、窗户、门、人偶等。你只需要从零件盒里拿出需要的零件,就能拼搭出城堡、汽车、飞机等各种各样的作品!

Python的“库“(Library)就像这个乐高零件盒。里面装满了别人已经写好的、经过测试的代码,我们可以直接拿来使用,而不需要从零开始编写每一个功能。这正是编程的精髓之一:站在巨人的肩膀上,用更少的代码做更多的事情!

给家长的小贴士 💡

  • 为什么需要库? 就像我们不需要自己制造铅笔、橡皮一样,编程也不需要从零开始写所有功能。库让编程变得高效、有趣且专业化。
  • 本章目标 让孩子了解库的概念,学会使用几个常用的库,并理解如何查找和学习新的库。
  • 编程思想 通过学习库,培养孩子的“代码复用“思维——理解为什么重复造轮子是低效的,而善用已有工具是聪明的做法。
  • 实践建议 每个库都有趣味性强的例子,鼓励孩子亲手运行代码并修改参数,观察效果变化。

什么是库?

在Python中,库是一组相关功能的集合。有些库是Python自带的(标准库),有些需要额外安装(第三方库)。

生活类比:工具箱的智慧 🔧

库的概念在生活中随处可见:

  • 工具箱 → 里面有锤子、螺丝刀、扳手等,你不需要自己制造这些工具
  • 材料包 → 做手工时,纸、胶水、剪刀等材料都准备好了
  • 乐高积木 → 各种预制的零件,可以直接拼搭作品
  • 调料架 → 做菜时,盐、糖、酱油等调料都已准备好

编程中的库也是一样的道理! 别人已经把常用功能写好了,我们只需要“导入“就能使用。

# 这就像从工具箱里拿出"随机数生成器"这个工具
import random

# 然后直接使用它
number = random.randint(1, 100)

编程思想:代码复用 ♻️

在编程中,有一个重要的原则叫做DRY(Don’t Repeat Yourself,不要重复自己)。库就是这个原则的最佳实践:

  • 避免重复劳动 → 不用每次都写相同的功能
  • 提高效率 → 把时间花在创造新功能上
  • 减少错误 → 使用经过测试的代码,比自己写更可靠
  • 便于维护 → 库会持续更新和改进

给家长的小贴士 💡

  • 模块化思维 库体现了“模块化“的编程思想——把复杂问题分解成可复用的小模块。这种思维方式不仅能应用于编程,也能应用于解决日常问题。
  • 分工协作 现实中的大型软件是由成百上千的程序员协作开发的,每个人负责不同的模块(库),最后组合成完整的系统。这就像建造大厦,不同工种的人负责不同的工作。
  • 教学建议 可以和孩子讨论生活中的“复用“例子,比如:预制菜、模板、复印等,帮助孩子理解“复用“的价值。

使用库的基本步骤

使用库通常需要两个步骤:

  1. 导入库 - 告诉Python我们要使用哪个库
  2. 调用功能 - 使用库提供的函数或对象
# 导入库
import random

# 使用库中的功能
print(random.randint(1, 100))

导入库的几种方式

# 方式1: 导入整个库(推荐给初学者)
import random
print(random.randint(1, 100))

# 方式2: 给库起一个简短的别名
import random as r
print(r.randint(1, 100))

# 方式3: 只导入库中的某个函数
from random import randint
print(randint(1, 100))

# 方式4: 导入库的所有内容(不推荐,容易造成名称冲突)
from random import *
print(randint(1, 100))

给家长的小贴士 💡

  • 导入方式的选择 对于初学者,推荐使用方式1(导入整个库),因为这样代码可读性更好,能清楚知道每个函数来自哪个库。
  • 命名冲突 方式4容易导致函数名冲突,不建议孩子使用。举例说明:如果两个库都有叫read()的函数,就会产生混淆。
  • 可读性优先 在学习阶段,代码清晰易懂比简洁更重要。鼓励孩子写出容易理解的代码。

库与计算机系统的关系 💻

系统库 vs 第三方库

Python的库可以分为两大类:

1. 标准库(系统库)

这些是Python自带的库,安装Python后就可以直接使用:

  • random - 生成随机数
  • time - 时间和计时
  • json - JSON数据格式
  • math - 数学函数(正弦、余弦、平方根等)
  • os - 操作系统功能(文件、目录等)

类比: 就像手机出厂时预装的应用程序(计算器、日历、时钟等)。

2. 第三方库

这些是由社区开发者编写的库,需要额外安装:

  • pyttsx3 - 文字转语音
  • pygame - 游戏开发
  • turtle - 图形绘制(有些Python发行版自带)
  • pandas - 数据分析
  • requests - 网络请求

类比: 就像从应用商店下载的额外应用程序(游戏、社交软件等)。

库与计算机硬件的协作

当我们使用库时,实际上是在指挥计算机的各个部件协同工作:

┌─────────────────────────────────────┐
│         你的程序                    │
│  import random                     │
│  number = random.randint(1, 100)   │
└────────────┬────────────────────────┘
             │ 调用
             ↓
┌─────────────────────────────────────┐
│      Random库(代码)                 │
│  包含生成随机数的算法                │
└────────────┬────────────────────────┘
             │ 使用
             ↓
┌─────────┬───┴────┬─────────┬───────┐
│   CPU   │  内存  │   时钟   │ 系统时间│
│ 执行算法│ 存储数据│ 提供种子│ 随机源 │
└─────────┴────────┴─────────┴───────┘

给家长的小贴士 💡

  • “随机“的本质 可以告诉孩子,计算机中的“随机数“其实不是真正随机的,而是根据一个“种子”(通常是当前时间)通过数学公式计算出来的。这就像“如果知道种子,就能预测结果“,所以叫“伪随机数“。
  • 硬件协作 每个库的函数最终都会转换成CPU指令,指挥硬件工作。让孩子理解“代码→CPU指令→硬件执行“这个流程。
  • 系统资源 有些库会消耗系统资源(内存、CPU时间),比如图形库需要显卡支持。这是很好的“成本意识“教育机会。

库的“生态系统“ 🌿

Python有庞大的库生态系统,这得益于:

  • 开源社区 → 全世界的程序员共同贡献代码
  • 包管理工具 → pip工具让安装库变得简单
  • 文档和教程 → 每个库都有详细的使用说明
  • 持续更新 → 库会不断改进和修复bug

这就像一个巨大的“工具共享社区“,每个人都可以使用别人的工具,也可以贡献自己的工具!

给家长的小贴士 💡

  • 开源精神 可以向孩子介绍“开源“的概念——代码共享、互相帮助、共同进步。这是现代软件行业的重要文化。
  • 社区协作 大型项目通常由世界各地的人协作开发,通过互联网共享代码。这能培养孩子的全球视野和协作意识。
  • 学习资源 教会孩子如何搜索和利用社区资源(文档、论坛、教程)是重要的自学能力。

Random库 - 生成随机数 🎲

Random库可以帮我们生成随机数,这对于制作游戏、模拟实验等都很有用。

生成随机整数

randint(a, b)函数可以生成a到b之间的随机整数(包含a和b)。

import random

# 生成1到100之间的随机数
secret_number = random.randint(1, 100)
print(f"神秘数字是: {secret_number}")

# 生成1到6之间的随机数(模拟掷骰子)
dice = random.randint(1, 6)
print(f"骰子点数: {dice}")

# 生成1到10之间的随机数
lucky = random.randint(1, 10)
print(f"幸运数字: {lucky}")

运行示例:

神秘数字是: 73
骰子点数: 4
幸运数字: 7

从列表中随机选择

choice()函数可以从一个列表中随机选择一个元素。

import random

fruits = ["苹果", "香蕉", "橙子", "葡萄", "西瓜"]

# 随机选择一个水果
fruit = random.choice(fruits)
print(f"今天吃: {fruit}")

# 随机选择3次
print("\n幸运抽奖:")
for i in range(3):
    prize = random.choice(fruits)
    print(f"第{i+1}次: {prize}")

运行示例:

今天吃: 葡萄

幸运抽奖:
第1次: 西瓜
第2次: 苹果
第3次: 橙子

数学练习:概率实验 📊

我们可以用random库来做数学中的概率实验!

import random

print("=== 抛硬币实验 ===")
heads = 0  # 正面次数
tails = 0  # 反面次数
total = 1000  # 抛1000次

for i in range(total):
    # 随机选择0或1,0代表正面,1代表反面
    result = random.randint(0, 1)
    if result == 0:
        heads += 1
    else:
        tails += 1

print(f"抛硬币{total}次的结果:")
print(f"正面(0): {heads}次, 比例: {heads/total*100:.1f}%")
print(f"反面(1): {tails}次, 比例: {tails/total*100:.1f}%")
print(f"\n理论上,正面和反面应该各占50%")
print(f"实验结果与理论的差异: {abs(heads - tails)/total*100:.1f}%")

运行示例:

=== 抛硬币实验 ===
抛硬币1000次的结果:
正面(0): 503次, 比例: 50.3%
反面(1): 497次, 比例: 49.7%

理论上,正面和反面应该各占50%
实验结果与理论的差异: 0.6%

给家长的小贴士 💡

  • 概率与统计 这个实验很好地展示了“大数定律“:当试验次数足够多时,实验结果会趋近于理论概率。
  • 数学联系 可以和孩子讨论:为什么是1000次而不是10次?实验次数越多,结果越接近50%。
  • 扩展思考 可以让孩子修改程序,尝试掷骰子实验,看看每个数字出现的概率是否接近1/6。

打乱列表顺序

shuffle()函数可以随机打乱列表中元素的顺序。

import random

cards = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]

print("原始顺序:", cards)

# 打乱顺序
random.shuffle(cards)
print("打乱后:", cards)

# 再次打乱
random.shuffle(cards)
print("再次打乱:", cards)

综合练习:猜数字游戏

import random

# 电脑随机生成一个1-100的数字
secret = random.randint(1, 100)
attempts = 0

print("=== 猜数字游戏 ===")
print("我已经想好了一个1到100之间的数字,你能在几次内猜中?")

while True:
    guess = int(input("请输入你的猜测(1-100): "))
    attempts += 1

    if guess == secret:
        print(f"🎉 恭喜你!第{attempts}次猜对了!")
        break
    elif guess < secret:
        print("太小了,再大一点!")
    else:
        print("太大了,再小一点!")

给家长的小贴士 💡

  • 随机数的概念 向孩子解释“随机“意味着每次运行结果可能不同,就像掷骰子一样。
  • 游戏化学习 猜数字游戏是练习循环和条件的绝佳例子,孩子会很有兴趣。
  • 调试技巧 可以让孩子打印出secret_number,先理解程序逻辑,再玩正式游戏。

练习1

练习1: 石头剪刀布游戏

编写一个石头剪刀布游戏:

  1. 电脑随机选择(石头、剪刀、布)
  2. 玩家输入选择
  3. 比较并显示结果

提示: 用random.choice()和if-elif语句

参考答案
import random

options = ["石头", "剪刀", "布"]

while True:
    # 电脑随机选择
    computer = random.choice(options)

    # 玩家选择
    player = input("请选择(石头/剪刀/布)或输入q退出: ")
    if player == "q":
        break

    if player not in options:
        print("无效的选择!")
        continue

    print(f"电脑出: {computer}")
    print(f"你出: {player}")

    # 判断胜负
    if player == computer:
        print("平局!")
    elif (player == "石头" and computer == "剪刀") or \
         (player == "剪刀" and computer == "布") or \
         (player == "布" and computer == "石头"):
        print("你赢了! 🎉")
    else:
        print("电脑赢了! 😢")
    print()

Time库 - 时间和计时 ⏰

Time库让我们能够处理时间相关的操作,比如暂停程序、计时、获取当前时间等。

暂停程序

sleep()函数可以让程序暂停指定的秒数。

import time

print("开始倒计时!")
print("3...")
time.sleep(1)
print("2...")
time.sleep(1)
print("1...")
time.sleep(1)
print("发射! 🚀")

print("\n模拟下载文件...")
for i in range(1, 6):
    print(f"下载中... {i*20}%")
    time.sleep(0.5)
print("下载完成!")

获取当前时间

time()函数返回当前时间的时间戳(从1970年1月1日开始的秒数)。

import time

# 获取当前时间戳
current_time = time.time()
print(f"当前时间戳: {current_time}")

# 转换为可读格式
readable_time = time.ctime(current_time)
print(f"可读时间: {readable_time}")

运行示例:

当前时间戳: 1736871234.5678901
可读时间: Mon Jan 15 14:32:14 2025

数学联系:时间计算 🧮

时间计算是很好的数学练习!

import time

# 记录开始时间
start = time.time()

# 做一些计算(比如计算1到10000的和)
total = 0
for i in range(1, 10001):
    total += i

# 记录结束时间
end = time.time()

# 计算耗时
elapsed = end - start

print(f"1到10000的和: {total}")
print(f"计算耗时: {elapsed:.6f}秒")

# 数学问题:如果计算1到100000的和,需要多久?
print("\n让我们试试计算1到100000的和...")

start = time.time()
total = 0
for i in range(1, 100001):
    total += i
end = time.time()

elapsed = end - start
print(f"计算耗时: {elapsed:.6f}秒")

# 思考题:耗时增加了大约多少倍?
print("\n思考:计算量增加了10倍,耗时增加了多少倍?")

计时器

perf_counter()函数可以用来精确计时,常用于测量程序运行时间。

import time

# 开始计时
start = time.perf_counter()

# 模拟一些工作
print("开始计算...")
sum_result = 0
for i in range(1, 100000001):
    sum_result += i

# 结束计时
end = time.perf_counter()

# 计算耗时
elapsed = end - start
print(f"1到1亿求和结果: {sum_result}")
print(f"耗时: {elapsed:.2f}秒")

# 数学问题:CPU每秒能执行多少次加法?
operations = 100000000 / elapsed
print(f"\nCPU每秒大约执行了 {operations:.0f} 次加法运算")

运行示例:

开始计算...
1到1亿求和结果: 5000000050000000
耗时: 4.23秒

CPU每秒大约执行了 23640662 次加法运算

给家长的小贴士 💡

  • CPU性能概念 通过计时,让孩子理解CPU的速度——每秒能执行几千万次简单运算!这能培养对计算机性能的直观认识。
  • 时间戳的概念 向孩子解释时间戳就像给每一刻都编了一个号码,方便计算机计算时间差。
  • 实际应用 计时功能可以用于测试程序效率,让孩子理解“优化“的概念——同样的功能,代码写得更好,运行更快。

综合练习:速度测试游戏

import time
import random

print("=== 打字速度测试 ===")
print("我会显示一个随机单词,你需要尽快输入它!")

words = ["python", "computer", "programming", "keyboard", "mouse", "screen"]
word = random.choice(words)

print(f"\n请输入: {word}")

# 开始计时
start = time.perf_counter()

user_input = input()

# 结束计时
end = time.perf_counter()
elapsed = end - start

if user_input == word:
    print(f"✓ 正确!耗时: {elapsed:.2f}秒")
    if elapsed < 1:
        print("速度: ⚡⚡⚡ 超级快!")
    elif elapsed < 2:
        print("速度: ⚡⚡ 很快!")
    elif elapsed < 3:
        print("速度: ⚡ 还可以!")
    else:
        print("速度: 🐢 需要练习哦!")
else:
    print("✗ 输入错误!")

练习2

练习2: 反应时间测试

编写一个测试反应时间的程序:

  1. 程序随机等待2-5秒
  2. 显示“现在按回车键!“
  3. 计算用户按回车键的反应时间

提示: 用random.randint()和time.perf_counter()

参考答案
import time
import random

print("=== 反应时间测试 ===")
print("当你看到'现在按回车键!'时,尽快按回车!")
input("准备好了吗?按回车开始...")

# 随机等待2-5秒
wait_time = random.randint(2, 5)
time.sleep(wait_time)

# 记录开始时间
start = time.perf_counter()

# 等待用户按下回车
input("现在按回车键!")

# 记录结束时间
end = time.perf_counter()

# 计算反应时间
reaction = end - start
print(f"\n你的反应时间: {reaction:.3f}秒")

if reaction < 0.3:
    print("神一般的反应! ⚡⚡⚡")
elif reaction < 0.5:
    print("很快! ⚡⚡")
elif reaction < 0.7:
    print("正常水平 ⚡")
else:
    print("有点慢...再接再厉! 🐢")

Turtle库 - 图形绘制(复习与扩展) 🐢

我们在第6章已经学习了Turtle库的基础,这里我们复习并学习一些高级功能。

填充颜色

begin_fill()和end_fill()函数可以让 turtle填充封闭图形的颜色。

import turtle

t = turtle.Turtle()
t.speed(1)

# 设置画笔颜色和填充颜色
# color()可以同时设置两个颜色
t.color("red", "yellow")  # 画笔红色,填充黄色

# 开始填充
t.begin_fill()

# 画一个五角星
for _ in range(5):
    t.forward(200)
    t.right(144)

# 结束填充
t.end_fill()

turtle.mainloop()

数学练习:多角星的几何计算 📐

画多角星需要计算角度,这是很好的几何练习!

import turtle

t = turtle.Turtle()
t.speed(0)

# 画一个50角星
t.color("red", "yellow")
t.begin_fill()

for _ in range(50):
    t.forward(200)
    t.left(170)  # 每次转170度

t.end_fill()

turtle.mainloop()

数学思考题:

  • 如果画n角星,每次应该转多少度?
  • 提示:360度 × (n-2) / n 是正n边形的内角
  • 星形的角度是:180 - (360 / n)

几何知识复习

import turtle

def draw_polygon(t, sides, size):
    """画正多边形"""
    # 计算外角
    angle = 360 / sides
    print(f"画{sides}边形,每次转{angle}度")

    for _ in range(sides):
        t.forward(size)
        t.right(angle)

t = turtle.Turtle()
t.speed(1)

# 画各种多边形
draw_polygon(t, 3, 100)  # 三角形
t.penup()
t.goto(150, 0)
t.pendown()

draw_polygon(t, 4, 100)  # 正方形
t.penup()
t.goto(-150, 0)
t.pendown()

draw_polygon(t, 5, 100)  # 五边形
t.penup()
t.goto(0, -150)
t.pendown()

draw_polygon(t, 6, 100)  # 六边形
t.penup()
t.goto(0, 150)
t.pendown()

draw_polygon(t, 8, 80)   # 八边形

turtle.mainloop()

给家长的小贴士 💡

  • 几何与编程 这是将编程与数学几何完美结合的例子!鼓励孩子计算不同多边形的角度。
  • 数学公式复习:
    • 三角形内角和 = 180度
    • 四边形内角和 = 360度
    • n边形内角和 = (n-2) × 180度
    • 正n边形每个外角 = 360度 / n
  • 探索精神 鼓励孩子尝试不同的参数,观察图形的变化,这是科学探索的精神!

在画布上写字

write()函数可以在画布上写字。

import turtle
import time

t = turtle.Turtle()
t.speed(1)

# 设置画笔大小和颜色
t.pensize(5)
t.pencolor("yellow")
t.fillcolor("red")

# 画一个五边形
t.begin_fill()
for _ in range(5):
    t.forward(200)
    t.right(144)
t.end_fill()

# 等待2秒
time.sleep(2)

# 抬起画笔,移动到指定位置
t.penup()
t.goto(-150, -120)

# 设置颜色并写字
t.color("violet")
t.write("Done!", font=('Arial', 40, 'normal'))

turtle.mainloop()

综合练习: 彩虹五角星

import turtle

t = turtle.Turtle()
t.speed(0)

# 定义彩虹颜色
colors = ["red", "orange", "yellow", "green", "blue", "purple"]

# 画多个五角星
for i in range(36):
    t.color(colors[i % 6])  # 循环使用颜色
    t.begin_fill()

    for _ in range(5):
        t.forward(100)
        t.right(144)

    t.end_fill()
    t.right(10)  # 每次旋转10度

turtle.mainloop()

给家长的小贴士 💡

  • 复习与巩固 这个部分是对第6章内容的复习,如果孩子已经很熟悉,可以快速跳过。
  • 颜色循环 colors[i % 6]这个表达式是一个重要的技巧,向孩子解释取余运算的作用。
  • 创意扩展 鼓励孩子修改参数(角度、步长、颜色),创造自己的图形艺术。

练习3

练习3: 花朵图案

使用Turtle画一朵花:

  1. 画多个花瓣(用椭圆或曲线)
  2. 每个花瓣旋转一定角度
  3. 中心填充黄色,花瓣用粉色
参考答案
import turtle

t = turtle.Turtle()
t.speed(0)

# 画花瓣
for _ in range(12):
    t.color("pink", "pink")
    t.begin_fill()

    # 画一个椭圆花瓣
    for _ in range(2):
        t.circle(50, 90)
        t.circle(10, 90)

    t.end_fill()
    t.right(30)  # 旋转30度

# 画花心
t.penup()
t.goto(0, -20)
t.pendown()
t.color("yellow", "yellow")
t.begin_fill()
t.circle(20)
t.end_fill()

turtle.mainloop()

文件操作库 - 数据的持久化 💾

文件操作让我们可以读取和保存数据,这样程序关闭后数据不会丢失。

为什么需要文件?

想象一下,如果你写的日记每次合上本子后,字迹就消失了,那该多糟糕!文件就是计算机的“日记本“,让数据可以永久保存。

# 没有文件:数据在内存中,程序关闭就丢失
score = 100  # 程序关闭后,这个数据就不见了

# 有了文件:数据保存在硬盘上,程序关闭后数据还在
f = open("score.txt", "w")
f.write("100")
f.close()
# 即使关闭程序,数据仍然保存在文件中

文件与计算机硬件 🖥️

┌─────────────────────────────────────┐
│         你的程序                    │
│  读取/保存数据                       │
└────────────┬────────────────────────┘
             │
             ↓
┌─────────────────────────────────────┐
│         文件系统                     │
│  管理文件的存储和组织                │
└────────────┬────────────────────────┘
             │
             ↓
┌─────────────────────────────────────┐
│          硬盘存储                   │
│  永久保存数据(磁道、扇区)             │
└─────────────────────────────────────┘

给家长的小贴士 💡

  • 内存 vs 硬盘 可以这样解释:
    • 内存 = 书桌 → 工作时放东西,速度快但断电后数据消失
    • 硬盘 = 文件柜 → 长期存储,速度慢但断电后数据还在
  • 持久化 向孩子解释“持久化“就是“让数据长期保存“的意思。
  • 文件编码 简单提一下文件是用0和1存储的,不同的编码方式(如UTF-8)决定如何表示字符。

打开和读取文件

open()函数用于打开文件,“r“表示只读模式。

# 打开文件
f = open("story.txt", "r")

# 读取全部内容
content = f.read()
print("文件内容:")
print(content)

# 关闭文件
f.close()

写入文件

“w“表示写入模式(会覆盖原有内容),“a“表示追加模式(在末尾添加)。

# 写入模式(覆盖)
f = open("diary.txt", "w")
f.write("2025年1月15日 天气: 晴\n")
f.write("今天我学会了Python的文件操作!\n")
f.close()

# 追加模式
f = open("diary.txt", "a")
f.write("感觉很有成就感! 😊\n")
f.close()

# 读取并显示
f = open("diary.txt", "r")
print(f.read())
f.close()

修改文件

# 打开文件进行读写("r+")
f = open("note.txt", "r+")

# 读取内容
content = f.read()
print("原始内容:")
print(content)

# 移动到文件开头
f.seek(0)

# 清空文件
f.truncate()

# 写入新内容
f.write("更新后的内容\n")
f.write("这是一行新文字\n")

# 关闭文件
f.close()

# 再次读取验证
f = open("note.txt", "r")
print("\n更新后内容:")
print(f.read())
f.close()

逐行读取

# 打开文件
f = open("students.txt", "r")

# 逐行读取
print("=== 学生名单 ===")
line_number = 1
for line in f:
    # 去除行尾的换行符
    name = line.strip()
    print(f"{line_number}. {name}")
    line_number += 1

f.close()

数学应用:成绩统计 📊

# 假设我们有一个成绩文件
# 创建示例文件
f = open("scores.txt", "w")
f.write("85\n")
f.write("92\n")
f.write("78\n")
f.write("95\n")
f.write("88\n")
f.close()

# 读取并统计
f = open("scores.txt", "r")
scores = []

for line in f:
    score = int(line.strip())
    scores.append(score)

f.close()

# 计算统计数据
total = sum(scores)
count = len(scores)
average = total / count
highest = max(scores)
lowest = min(scores)

print(f"成绩统计:")
print(f"总人数: {count}")
print(f"总分: {total}")
print(f"平均分: {average:.1f}")
print(f"最高分: {highest}")
print(f"最低分: {lowest}")

给家长的小贴士 💡

  • 文件路径 默认情况下,文件会在当前目录创建。可以教孩子使用绝对路径。
  • 文件编码 如果遇到中文乱码,可以在open()时指定encoding="utf-8"
  • 关闭文件 强调f.close()的重要性,就像用完水龙头要关水一样。
  • 更好的写法 可以介绍with语句,它会自动关闭文件:
# 推荐的写法:自动关闭文件
with open("data.txt", "r") as f:
    content = f.read()
# 文件会自动关闭,即使发生错误也是如此

练习4

练习4: 成绩记录本

编写一个成绩记录程序:

  1. 可以输入科目和成绩
  2. 保存到文件
  3. 可以查看所有历史记录
参考答案
print("=== 成绩记录本 ===")

while True:
    print("\n1. 记录成绩")
    print("2. 查看历史")
    print("3. 退出")

    choice = input("请选择(1-3): ")

    if choice == "1":
        subject = input("科目: ")
        score = input("成绩: ")

        # 追加到文件
        f = open("scores.txt", "a")
        f.write(f"{subject}: {score}\n")
        f.close()
        print("✓ 已保存!")

    elif choice == "2":
        try:
            f = open("scores.txt", "r")
            print("\n=== 历史成绩 ===")
            print(f.read())
            f.close()
        except FileNotFoundError:
            print("还没有任何记录!")

    elif choice == "3":
        break

JSON库 - 数据交换格式 📋

JSON是一种常用的数据格式,Python的json库可以读写JSON文件。

什么是JSON?

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。

{
    "name": "小明",
    "age": 10,
    "hobbies": ["篮球", "编程", "音乐"]
}

生活类比:

  • JSON就像乐高组装说明书 → 用统一格式描述如何组装
  • 就像简历模板 → 用统一格式记录个人信息
  • 就像配置表 → 记录软件的各种设置

JSON与数据结构 📊

JSON完美对应Python的数据结构:

# Python字典 → JSON对象
person = {
    "name": "小明",
    "age": 10
}

# Python列表 → JSON数组
hobbies = ["篮球", "编程", "音乐"]

# Python字符串/数字/布尔值 → JSON对应类型
name = "小明"
age = 10
is_student = True

读取JSON文件

import json

# 读取JSON文件
f = open("student.json", "r")
student = json.load(f)
f.close()

# 使用数据
print(f"姓名: {student['name']}")
print(f"年龄: {student['age']}")
print("爱好:")
for hobby in student['hobbies']:
    print(f"  - {hobby}")

写入JSON文件

import json

# 准备数据
student = {
    "name": "小红",
    "age": 11,
    "grade": "五年级",
    "hobbies": ["画画", "跳舞", "阅读"]
}

# 写入JSON文件
f = open("student.json", "w")
# indent=2 表示缩进2个空格,让格式更美观
json.dump(student, f, indent=2, ensure_ascii=False)
f.close()

print("JSON文件已创建!")

修改JSON文件

import json

# 读取JSON文件
f = open("data.json", "r+")
data = json.load(f)

# 修改数据
data['age'] = 12
data['hobbies'].append("游泳")

# 移动到文件开头
f.seek(0)

# 清空文件
f.truncate()

# 写入修改后的数据
json.dump(data, f, indent=2, ensure_ascii=False)
f.close()

print("数据已更新!")

数学应用:成绩统计系统 📈

import json

# 保存班级成绩
class_data = {
    "class_name": "五年级1班",
    "students": [
        {"name": "小明", "scores": {"数学": 95, "语文": 88, "英语": 92}},
        {"name": "小红", "scores": {"数学": 89, "语文": 95, "英语": 90}},
        {"name": "小刚", "scores": {"数学": 92, "语文": 85, "英语": 88}}
    ]
}

# 保存到文件
f = open("class_scores.json", "w", encoding="utf-8")
json.dump(class_data, f, indent=2, ensure_ascii=False)
f.close()

# 读取并统计
f = open("class_scores.json", "r", encoding="utf-8")
data = json.load(f)
f.close()

print(f"班级: {data['class_name']}")
print(f"学生人数: {len(data['students'])}")

# 计算班级平均分
math_total = 0
chinese_total = 0
english_total = 0

for student in data['students']:
    scores = student['scores']
    math_total += scores['数学']
    chinese_total += scores['语文']
    english_total += scores['英语']

count = len(data['students'])
print(f"\n班级平均分:")
print(f"数学: {math_total/count:.1f}")
print(f"语文: {chinese_total/count:.1f}")
print(f"英语: {english_total/count:.1f}")

给家长的小贴士 💡

  • JSON的优势 JSON格式易读、通用,很多网站和API都使用JSON格式交换数据。
  • ensure_ascii=False 这个参数让中文字符正常显示,而不是显示成Unicode编码。
  • 应用场景 可以用JSON保存游戏进度、配置文件等。
  • 数据结构映射 这是一个很好的机会,向孩子展示现实中的数据如何用编程结构来表示。

练习5

练习5: 个人信息管理

编写一个个人信息管理系统:

  1. 可以查看信息
  2. 可以修改姓名
  3. 可以添加爱好
  4. 保存到JSON文件
参考答案
import json
import os

filename = "my_info.json"

# 检查文件是否存在
if os.path.exists(filename):
    f = open(filename, "r")
    info = json.load(f)
    f.close()
    print("找到已有信息!")
else:
    info = {}
    print("创建新档案...")

while True:
    print("\n=== 个人信息管理 ===")
    print("1. 查看信息")
    print("2. 修改姓名")
    print("3. 添加爱好")
    print("4. 保存并退出")

    choice = input("请选择(1-4): ")

    if choice == "1":
        print("\n当前信息:")
        for key, value in info.items():
            print(f"{key}: {value}")

    elif choice == "2":
        name = input("请输入姓名: ")
        info['name'] = name
        print("✓ 姓名已更新!")

    elif choice == "3":
        hobby = input("请输入新爱好: ")
        if 'hobbies' not in info:
            info['hobbies'] = []
        info['hobbies'].append(hobby)
        print("✓ 爱好已添加!")

    elif choice == "4":
        f = open(filename, "w")
        json.dump(info, f, indent=2, ensure_ascii=False)
        f.close()
        print("✓ 信息已保存!再见!")
        break

自己开发库 - 模块化编程 🧩

我们不仅可以使用别人写的库,还可以自己创建库!把常用的功能打包成库,可以让代码更简洁、更易维护。

为什么要自己写库?

代码复用的思想:

# 没有库:每次都要重复写相同的代码
def calculate_rectangle_area(length, width):
    return length * width

def calculate_rectangle_perimeter(length, width):
    return 2 * (length + width)

# 在多个程序中重复复制这些代码... 😞

# 有了库:写一次,到处使用
import my_tools
area = my_tools.calculate_rectangle_area(10, 5)  # 😊

好处:

  • ✅ 不用重复写代码
  • ✅ 代码更简洁
  • ✅ 更新时只改一个地方
  • ✅ 可以分享给别人使用

创建自己的库

创建一个名为my_tools.py的文件:

# my_tools.py - 我的工具库

def calculate_rectangle_area(length, width):
    """计算长方形面积"""
    return length * width

def calculate_rectangle_perimeter(length, width):
    """计算长方形周长"""
    return 2 * (length + width)

def say_hello(name):
    """打招呼函数"""
    return f"你好, {name}!"

def get_grade(score):
    """根据分数返回等级"""
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    elif score >= 60:
        return "D"
    else:
        return "F"

使用自己的库

在另一个程序中导入并使用:

# main.py
import my_tools

# 使用库中的函数
length = 10
width = 5

area = my_tools.calculate_rectangle_area(length, width)
perimeter = my_tools.calculate_rectangle_perimeter(length, width)

print(f"长方形面积: {area}")
print(f"长方形周长: {perimeter}")

# 问候
greeting = my_tools.say_hello("小明")
print(greeting)

# 成绩等级
score = 85
grade = my_tools.get_grade(score)
print(f"分数{score}对应的等级是: {grade}")

数学工具库示例 🧮

创建一个math_tools.py文件:

# math_tools.py - 数学工具库

def calculate_average(numbers):
    """计算平均数"""
    if len(numbers) == 0:
        return 0
    return sum(numbers) / len(numbers)

def calculate_median(numbers):
    """计算中位数"""
    if len(numbers) == 0:
        return 0

    sorted_numbers = sorted(numbers)
    n = len(sorted_numbers)
    middle = n // 2

    if n % 2 == 0:
        # 偶数个元素,取中间两个的平均值
        return (sorted_numbers[middle-1] + sorted_numbers[middle]) / 2
    else:
        # 奇数个元素,取中间的值
        return sorted_numbers[middle]

def is_prime(n):
    """判断是否为质数"""
    if n < 2:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False

    for i in range(3, int(n**0.5) + 1, 2):
        if n % i == 0:
            return False
    return True

def calculate_factorial(n):
    """计算阶乘"""
    if n < 0:
        return None
    if n == 0 or n == 1:
        return 1

    result = 1
    for i in range(2, n + 1):
        result *= i
    return result

使用这个数学工具库:

import math_tools

# 测试平均数
scores = [85, 92, 78, 95, 88]
avg = math_tools.calculate_average(scores)
print(f"平均分: {avg}")

# 测试中位数
median = math_tools.calculate_median(scores)
print(f"中位数: {median}")

# 测试质数判断
print(f"17是质数吗? {math_tools.is_prime(17)}")
print(f"18是质数吗? {math_tools.is_prime(18)}")

# 测试阶乘
print(f"5的阶乘: {math_tools.calculate_factorial(5)}")

综合练习: 图形工具库

创建一个drawing_tools.py文件:

# drawing_tools.py - 绘图工具库
import turtle

def draw_square(t, size):
    """画正方形"""
    for _ in range(4):
        t.forward(size)
        t.right(90)

def draw_triangle(t, size):
    """画三角形"""
    for _ in range(3):
        t.forward(size)
        t.right(120)

def draw_polygon(t, sides, size):
    """画多边形"""
    angle = 360 / sides
    for _ in range(sides):
        t.forward(size)
        t.right(angle)

def draw_star(t, size, points):
    """画星星"""
    angle = 180 - (180 / points)
    for _ in range(points):
        t.forward(size)
        t.right(angle)

使用这个库:

import turtle
import drawing_tools

t = turtle.Turtle()
t.speed(0)

# 使用库中的函数画图
drawing_tools.draw_square(t, 100)
t.penup()
t.goto(150, 0)
t.pendown()

drawing_tools.draw_triangle(t, 100)
t.penup()
t.goto(-150, 0)
t.pendown()

drawing_tools.draw_polygon(t, 6, 80)  # 六边形
t.penup()
t.goto(0, -150)
t.pendown()

drawing_tools.draw_star(t, 100, 5)  # 五角星

turtle.mainloop()

给家长的小贴士 💡

  • 模块化思维 教孩子把常用的功能整理成库,培养模块化的思维。
  • 文件组织 建议创建一个专门的文件夹存放自定义库。
  • 文档注释 在函数中使用三引号注释,说明函数的用途。
  • 函数命名 鼓励孩子使用清晰的函数名,让别人一看就知道函数是做什么的。
  • 编程规范 这是培养良好编程习惯的好机会,比如:
    • 一个函数只做一件事
    • 函数名要描述性
    • 添加注释说明

练习6

练习6: 语音工具库

创建一个语音工具库speech_tools.py,包含以下函数:

  1. speak_text(text) - 读出文字
  2. speak_number(number) - 读出数字
  3. speak_list(items) - 读出列表中的每一项

然后编写一个程序使用这个库。

参考答案

speech_tools.py:

import pyttsx3

def speak_text(text):
    """读出文字"""
    engine = pyttsx3.init()
    engine.setProperty('rate', 150)
    engine.say(text)
    engine.runAndWait()

def speak_number(number):
    """读出数字"""
    engine = pyttsx3.init()
    engine.setProperty('rate', 150)
    engine.say(f"数字是 {number}")
    engine.runAndWait()

def speak_list(items):
    """读出列表中的每一项"""
    engine = pyttsx3.init()
    engine.setProperty('rate', 150)

    for item in items:
        engine.say(item)
        engine.runAndWait()

使用示例:

import speech_tools

# 读文字
speech_tools.speak_text("你好,欢迎使用语音工具库")

# 读数字
speech_tools.speak_number(42)

# 读列表
fruits = ["苹果", "香蕉", "橙子"]
speech_tools.speak_list(fruits)

自学库 - 探索更多可能 🔍

Python有海量的第三方库,我们可以根据需要学习使用新的库。

如何查找和安装库

  1. 查找库 访问 https://pypi.org 搜索需要的库
  2. 安装库 使用pip3 install 库名安装
  3. 学习使用 阅读库的文档和示例代码

学会阅读文档 📖

这是程序员最重要的技能之一!

文档通常包含:

  • 安装说明 → 如何安装库
  • 快速开始 → 最简单的使用示例
  • API参考 → 所有函数的详细说明
  • 示例代码 → 完整的使用案例
  • 常见问题 → FAQ

阅读文档的技巧:

  1. 先看“快速开始“,跑通最简单的例子
  2. 再看示例代码,理解如何使用
  3. 遇到问题时查API参考
  4. 最后查看FAQ或搜索问题

给家长的小贴士 💡

  • 自学能力 学会查找和使用新库是重要的编程技能。
  • 文档阅读 教孩子如何阅读库的文档,找到需要的函数。
  • 试错精神 鼓励孩子多尝试,不怕犯错,从错误中学习。
  • 搜索引擎 教会孩子如何有效地搜索问题,比如:
    • “python 库名 教程”
    • “python 库名 example”
    • “python how to 使用某个功能”
  • 社区资源 介绍一些学习资源:
    • Stack Overflow - 问答社区
    • GitHub - 查看开源项目
    • B站/YouTube - 视频教程

实践挑战:探索新库

这里给你一个挑战:自己找一个可以播放音乐的Python库,学习它的接口,编写一个简单的音乐播放器。

推荐库:

  • pygame - 强大的多媒体库
  • playsound - 简单的音频播放
  • pydub - 音频处理库

示例步骤:

  1. 使用pip3 install pygame安装
  2. 在网上搜索“pygame music player example“
  3. 学习基本的播放功能
  4. 编写自己的播放器程序

学习过程记录:

[ ] 1. 安装库
[ ] 2. 查看官方文档
[ ] 3. 运行示例代码
[ ] 4. 理解代码原理
[ ] 5. 修改和扩展功能
[ ] 6. 完成自己的项目

常见错误和调试 🔧

错误1: ModuleNotFoundError

import nonexistent_module

错误信息: ModuleNotFoundError: No module named 'nonexistent_module'

原因: 库不存在或未安装

解决方法:

  • 检查库名是否拼写正确
  • 使用pip3 install 库名安装库

错误2: 导入路径错误

import my_tools  # 假设my_tools.py不在当前目录

错误信息: ModuleNotFoundError: No module named 'my_tools'

原因: Python找不到自定义库文件

解决方法:

  • 确保库文件和程序在同一目录
  • 或将库文件放在Python能找到的目录中

错误3: 文件未关闭

f = open("data.txt", "r")
content = f.read()
# 忘记 f.close()

问题: 文件可能被锁定,其他程序无法访问

解决方法: 使用with语句自动关闭文件

with open("data.txt", "r") as f:
    content = f.read()
# 文件会自动关闭

调试技巧

  1. 打印导入的库
import random
print(random)  # 检查是否成功导入
  1. 查看库的内容
import random
print(dir(random))  # 查看库中的所有函数
  1. 查看函数帮助
import random
help(random.randint)  # 查看函数说明

章节小结

核心知识点回顾

  1. 库的概念 🎁

    • 库是预先写好的代码集合,可以直接使用
    • 就像工具箱、乐高积木、材料包
    • 体现了“代码复用“和“模块化“的编程思想
  2. 导入库 📥

    • 使用import语句导入库
    • 有多种导入方式,推荐初学者使用import 库名
  3. 常用库 🛠️

    • random - 生成随机数
    • time - 时间和计时
    • turtle - 图形绘制
    • json - JSON数据格式
  4. 文件操作 💾

    • 读取和写入文件
    • 文件是数据的持久化存储
    • 理解内存和硬盘的区别
  5. 库与计算机系统 💻

    • 标准库 vs 第三方库
    • 库与硬件的协作关系
    • 开源社区和代码共享精神
  6. 自定义库 🧩

    • 可以自己创建库
    • 提高代码复用性
    • 培养模块化思维
  7. 自学能力 🔍

    • 如何查找和安装新库
    • 如何阅读文档
    • 从错误中学习

能力检查表 ✅

完成本章学习后,你应该能够:

  • 理解库的概念和作用
  • 正确导入和使用库
  • 使用random库生成随机数
  • 使用time库进行计时和暂停
  • 使用turtle库绘制图形
  • 进行基本的文件操作
  • 读写JSON文件
  • 创建和使用自定义库
  • 理解库与计算机硬件的关系
  • 会查找和学习新的库

编程思想总结 💡

通过学习库,我们掌握了重要的编程思想:

  • 代码复用 → 不要重复造轮子,善用已有工具
  • 模块化 → 把复杂问题分解成可复用的小模块
  • 分工协作 → 大型项目由多人分工完成,各自负责不同的库
  • 开源精神 → 代码共享,互相帮助,共同进步
  • 持续学习 → 技术在不断进步,要学会查找和探索新工具

数学知识点回顾 📚

本章融入的数学知识:

  • 概率统计 → 抛硬币实验、随机数分布
  • 几何计算 → 多边形角度、图形绘制
  • 数据分析 → 平均数、中位数、方差
  • 时间计算 → 时间戳、时间差
  • 函数概念 → 数学函数 vs 编程函数

计算机知识回顾 💻

本章融入的计算机知识:

  • 内存 vs 硬盘 → 临时存储 vs 永久存储
  • 文件系统 → 文件的组织和管理
  • CPU性能 → 通过计时理解运算速度
  • 随机数原理 → 种子和伪随机数
  • 开源生态 → 社区协作和代码共享

下一章预告 ➡️

本章我们学习了如何使用各种库来扩展程序的功能,理解了代码复用和模块化的重要思想。

下一章,我们将综合运用所学知识,开发一个命令行程序,实现一个实用的课表查询系统!我们将深入学习:

  • 如何设计一个完整的程序
  • 如何处理复杂的用户交互
  • 如何组织和管理大量数据
  • 文件系统在程序中的应用

挑战练习 🎯

  1. 抽奖系统 🎰 使用random库创建一个抽奖系统,可以输入参与者名单,随机抽取幸运儿。

    • 提示:用列表存储名单,用random.choice()抽取
  2. 语音闹钟 ⏰ 结合time和pyttsx3库,创建一个定时播报提醒的程序。

    • 提示:用time.sleep()等待,用pyttsx3播报
  3. 图形计算器 🖥️ 使用turtle库创建一个图形化的计算器界面。

    • 提示:用turtle画按钮,处理用户输入
  4. 数据管理器 📊 使用JSON文件创建一个个人数据管理系统,可以增删改查数据。

    • 提示:用字典存储数据,用json.dump/load保存读取
  5. 创意项目 ⭐ 自学一个新的Python库,用它创建一个有趣的项目!

    • 推荐方向:
      • ** Arcade** - 游戏开发库
      • ** Pillow** - 图像处理库
      • requests - 网络请求库
      • ** Beautiful Soup** - 网页爬虫库

恭喜你完成了第13章的学习! 🎉

你已经掌握了使用库这一重要技能,这会让你的编程之旅更加高效和有趣!继续保持好奇心和探索精神,下一章见! 👋

CommandLine程序

引言

同学们,你们有没有想过,我们每天在学校上课时,如果能有一个“电子小助手“,随时告诉我们今天有什么课,那该多好啊?

在前面几章中,我们已经学习了很多Python知识:

  • 第2章学会了输入和输出
  • 第5章学会了布尔值判断
  • 第7章学会了条件语句
  • 第8章学会了循环语句
  • 第10章学会了列表
  • 第11章学会了字典
  • 第12章学会了函数
  • 第13章学会了各种库的使用

现在,是时候把这些知识都运用起来,开发一个真正的实用程序了!

我们将开发一个智能课表查询系统,它可以:

  1. 📅 查询某天的课程
  2. ➕ 添加新的课程
  3. ✏️ 修改已有课程
  4. ❌ 删除不要的课程
  5. 📁 自动保存到文件,下次打开还能用
  6. 🗣️ 理解各种说法(周一/星期一/礼拜一都可以)
  7. 🕐 支持上午下午的区分
  8. 📆 支持直接输入日期(如“10月15日“)

这个程序将会像我们平时和机器人聊天一样自然,这就是**命令行程序(CommandLine Program)**的魅力!


什么是命令行程序?

命令行程序是一种通过文字命令和计算机交互的程序。没有漂亮的按钮和窗口,所有的操作都通过输入命令来完成。

🖥️ 计算机小知识:命令行的历史

你知道吗?在很久以前(1970-1980年代),计算机没有图形界面,没有鼠标,没有漂亮的窗口!

那时的程序员只能通过命令行(Command Line) 和计算机交流:

  • 屏幕是黑底的,只有白色的文字
  • 没有鼠标,只能用键盘输入命令
  • 每个命令都要准确记忆准确拼写

为什么那时候没有图形界面呢?

  • 计算机的CPU速度慢,处理不动复杂的图形
  • 内存很小,存不下那么多图片和界面
  • 硬盘很小,存不了那么多数据

所以命令行程序是计算机发展早期的重要交互方式!即使现在有了图形界面,命令行仍然被程序员广泛使用,因为它:

  • ✅ 运行速度快(不需要处理图形)
  • ✅ 占用资源少(不需要加载图片)
  • ✅ 灵活强大(可以组合各种命令)

命令行程序 vs 图形界面程序

特点命令行程序图形界面程序
交互方式输入文字命令点击按钮、菜单
外观黑色背景,白色文字有图片、按钮、窗口
学习难度需要记住命令直观,容易上手
灵活性非常灵活功能固定
运行速度相对慢
CPU占用
内存占用
例子我们的课表系统游戏、手机APP

生活中的命令行程序

其实你早就用过命令行程序了!

例子1: 和聊天机器人对话

你: 今天天气怎么样?
机器人: 今天北京晴,温度15-25度
你: 明天呢?
机器人: 明天多云,温度18-28度

例子2: 智能音箱

你: 小爱同学,播放音乐
音箱: 好的,正在播放你喜欢的歌
你: 下一首
音箱: 正在切换到下一首

这些都和命令行程序类似:你用文字告诉它要做什么,它用文字回应你!

Python中的命令行程序

在Python中,我们主要使用以下函数来实现命令行程序:

  1. input() - 获取用户的输入
  2. print() - 显示信息给用户
  3. 条件语句 - 根据用户的输入判断要做什么
  4. 循环语句 - 让程序持续运行,多次接收命令
  5. 字典/列表 - 存储数据(如课表)
  6. 文件操作 - 保存数据到文件
  7. 时间库 - 处理日期和时间
  8. 正则表达式 - 理解各种说法(周一/星期一/礼拜一)

第一步:最简单的课表查询系统

让我们从最简单的开始:只能查询,不能修改,程序退出后数据就丢失了。

程序1:固定课表查询

# 最简单的课表查询程序
# 使用字典存储课表信息

# 定义课表字典
schedule = {
    "周一": "语文、数学、英语",
    "周二": "数学、语文、体育",
    "周三": "英语、科学、美术",
    "周四": "音乐、数学、语文",
    "周五": "体育、英语、班会"
}

# 欢迎信息
print("=" * 40)
print("       欢迎使用课表查询系统")
print("=" * 40)
print("支持查询: 周一 到 周日")
print("输入 '退出' 结束程序")
print("=" * 40)

# 主循环
while True:
    # 获取用户输入
    day = input("\n请输入要查询的星期: ")

    # 判断是否要退出
    if day == "退出":
        print("谢谢使用,再见!")
        break

    # 查询课表
    if day in schedule:
        print(f"\n{day}的课程是: {schedule[day]}")
    else:
        print(f"\n抱歉,{day}没有课,好好休息吧!")

运行示例:

========================================
       欢迎使用课表查询系统
========================================
支持查询: 周一 到 周日
输入 '退出' 结束程序
========================================

请输入要查询的星期: 周一

周一的课程是: 语文、数学、英语

请输入要查询的星期: 周六

抱歉,周六没有课,好好休息吧!

请输入要查询的星期: 退出
谢谢使用,再见!

给家长的小贴士

教学重点:

  1. 字典的键值对: schedule[day] 通过星期几(键)找到课程(值)
  2. in 操作符: if day in schedule 判断这个键是否存在
  3. while True 循环: 让程序持续运行,直到用户输入“退出“
  4. break 语句: 跳出循环,结束程序

常见问题:

  • : 为什么用字典不用列表?

  • : 字典可以通过“周一“直接找到课程,列表需要遍历查找,字典更方便!

  • : while True 不是会永远循环吗?

  • : 是的,但我们在循环中用 break 跳出来,所以不会永远运行

扩展思考:

  • 如果课程有很多节,用字符串存储不方便怎么办?(提示:用列表)
  • 如何让程序同时识别“周一“、“星期一”、“礼拜一”?

练习1:完善基本课表查询

请完成以下任务:

任务: 修改上面的程序,让它:

  1. 用列表存储每天的课程(因为可能有多节课)
  2. 增加周末的课程(周六、周日)
🔍 查看答案
# 用列表存储每天的课程
schedule = {
    "周一": ["语文", "数学", "英语"],
    "周二": ["数学", "语文", "体育"],
    "周三": ["英语", "科学", "美术"],
    "周四": ["音乐", "数学", "语文"],
    "周五": ["体育", "英语", "班会"],
    "周六": [],  # 空列表表示没有课
    "周日": []   # 空列表表示没有课
}

print("=" * 40)
print("       欢迎使用课表查询系统")
print("=" * 40)
print("支持查询: 周一 到 周日")
print("输入 '退出' 结束程序")
print("=" * 40)

while True:
    day = input("\n请输入要查询的星期: ")

    if day == "退出":
        print("谢谢使用,再见!")
        break

    if day in schedule:
        courses = schedule[day]
        if courses:  # 列表不为空
            print(f"\n{day}的课程有: {', '.join(courses)}")
        else:  # 列表为空
            print(f"\n{day}没有课,好好休息吧!")
    else:
        print(f"\n输入错误,请输入周一到周日之间!")

改进说明:

  1. 每天的课程用列表存储: ["语文", "数学", "英语"]
  2. if courses: 判断列表是否为空
  3. ', '.join(courses) 把列表变成字符串,用逗号分隔

第二步:理解同义词(周一/星期一/礼拜一)

用户可能会说:

  • “周一”
  • “星期一”
  • “礼拜一”
  • “周1”

这些都表示同一天!我们需要让程序理解它们是同一个意思。

方法1:用多个键指向同一个值

# 用多个键指向同一个列表
courses_monday = ["语文", "数学", "英语"]

schedule = {
    "周一": courses_monday,
    "星期一": courses_monday,
    "礼拜一": courses_monday,
    "周1": courses_monday,
    "1": courses_monday,
    # ... 其他天的课程
}

print("支持的输入: 周一/星期一/礼拜一/周1/1")
while True:
    day = input("\n请输入要查询的星期: ")

    if day == "退出":
        break

    if day in schedule:
        courses = schedule[day]
        if courses:
            print(f"课程有: {', '.join(courses)}")
        else:
            print("今天没有课!")
    else:
        print("输入错误,请重新输入!")

优点: 简单易懂 缺点: 要写很多重复的内容


方法2:用函数转换输入(推荐)

更好的方法是:把用户的各种说法,统一转换成标准格式(如“周一“)。

# 创建一个同义词映射表
synonyms = {
    # 星期一的同义词
    "周一": "周一", "星期一": "周一", "礼拜一": "周一",
    "周1": "周一", "星期1": "周一", "礼拜1": "周一", "1": "周一",
    # 星期二
    "周二": "周二", "星期二": "周二", "礼拜二": "周二",
    "周2": "周二", "星期2": "周二", "礼拜2": "周二", "2": "周二",
    # 星期三
    "周三": "周三", "星期三": "周三", "礼拜三": "周三",
    "周3": "周三", "星期3": "周三", "礼拜3": "周三", "3": "周三",
    # 星期四
    "周四": "周四", "星期四": "周四", "礼拜四": "周四",
    "周4": "周四", "星期4": "周四", "礼拜4": "周四", "4": "周四",
    # 星期五
    "周五": "周五", "星期五": "周五", "礼拜五": "周五",
    "周5": "周五", "星期5": "周五", "礼拜5": "周五", "5": "周五",
    # 星期六
    "周六": "周六", "星期六": "周六", "礼拜六": "周六",
    "周6": "周六", "星期6": "周六", "礼拜6": "周六", "6": "周六",
    # 星期日
    "周日": "周日", "星期日": "周日", "礼拜日": "周日", "礼拜天": "周日", "星期天": "周日",
    "周7": "周日", "星期7": "周日", "礼拜7": "周日", "7": "周日", "周0": "周日",
}

# 定义课表(只有标准格式)
schedule = {
    "周一": ["语文", "数学", "英语"],
    "周二": ["数学", "语文", "体育"],
    "周三": ["英语", "科学", "美术"],
    "周四": ["音乐", "数学", "语文"],
    "周五": ["体育", "英语", "班会"],
    "周六": [],
    "周日": []
}

print("=" * 40)
print("       欢迎使用课表查询系统")
print("=" * 40)
print("支持多种说法: 周一/星期一/礼拜一/周1/1")
print("输入 '退出' 结束程序")
print("=" * 40)

while True:
    day_input = input("\n请输入要查询的星期: ")

    if day_input == "退出":
        print("谢谢使用,再见!")
        break

    # 转换用户输入
    if day_input in synonyms:
        day = synonyms[day_input]  # 转换成标准格式
        courses = schedule[day]
        if courses:
            print(f"\n{day}的课程有: {', '.join(courses)}")
        else:
            print(f"\n{day}没有课,好好休息吧!")
    else:
        print("\n输入错误,请重新输入!")

运行示例:

请输入要查询的星期: 礼拜一

周一的课程有: 语文, 数学, 英语

请输入要查询的星期: 星期5

周五的课程有: 体育, 英语, 班会

请输入要查询的星期: 8
输入错误,请重新输入!

给家长的小贴士

教学重点:

  1. 同义词映射: 用 synonyms 字典把各种说法映射到标准格式
  2. 分离数据和逻辑: schedule 只存储标准格式,不存储所有同义词
  3. 查表转换: day = synonyms[day_input] 把用户输入转换成标准格式

优点:

  • 代码更清晰,易于维护
  • 增加新的同义词很容易
  • 课表数据不冗余

扩展思考:

  • 如果用户输入“星斯一“(错别字),怎么办?(提示:可以用模糊匹配或提示正确写法)
  • 如何让程序记住用户最常用的说法,下次优先显示?

练习2:增加更多同义词

任务: 修改上面的程序,增加以下同义词的支持:

  • “monday”, “Mon”, “mon” (英文)
  • “星期天” (和“星期日“都指周日)
🔍 查看答案
# 同义词映射表
synonyms = {
    # 星期一
    "周一": "周一", "星期一": "周一", "礼拜一": "周一",
    "周1": "周一", "星期1": "周一", "礼拜1": "周一", "1": "周一",
    "monday": "周一", "mon": "周一", "Mon": "周一",
    # 星期二
    "周二": "周二", "星期二": "周二", "礼拜二": "周二",
    "周2": "周二", "星期2": "周二", "礼拜2": "周二", "2": "周二",
    "tuesday": "周二", "tue": "周二", "Tue": "周二",
    # ... (星期三到星期五类似)
    # 星期日
    "周日": "周日", "星期日": "周日", "星期天": "周日",
    "礼拜日": "周日", "礼拜天": "周日",
    "周7": "周日", "星期7": "周日", "礼拜7": "周日", "7": "周日", "周0": "周日",
    "sunday": "周日", "sun": "周日", "Sun": "周日",
}

# 定义课表
schedule = {
    "周一": ["语文", "数学", "英语"],
    "周二": ["数学", "语文", "体育"],
    "周三": ["英语", "科学", "美术"],
    "周四": ["音乐", "数学", "语文"],
    "周五": ["体育", "英语", "班会"],
    "周六": [],
    "周日": []
}

print("=" * 40)
print("       欢迎使用课表查询系统")
print("=" * 40)
print("支持中文/英文: 周一/星期一/monday/mon")
print("输入 '退出' 结束程序")
print("=" * 40)

while True:
    day_input = input("\n请输入要查询的星期: ").lower()  # 转换成小写

    if day_input == "退出":
        print("谢谢使用,再见!")
        break

    if day_input in synonyms:
        day = synonyms[day_input]
        courses = schedule[day]
        if courses:
            print(f"\n{day}的课程有: {', '.join(courses)}")
        else:
            print(f"\n{day}没有课,好好休息吧!")
    else:
        print("\n输入错误,请重新输入!")

改进说明:

  1. synonyms 中增加英文同义词
  2. 使用 .lower() 把用户输入转换成小写,这样“Monday“和“monday“都能识别
  3. 增加了“星期天“的同义词

第三步:支持上午和下午

学校的课表通常会区分上午和下午,让我们来实现这个功能。

⏰ 数学小知识:时间计算

在编写课表程序时,我们会用到很多时间计算的知识!

小学数学知识点:时间单位换算

1小时 = 60分钟
1分钟 = 60秒
半天 = 4节课 (每节40-45分钟)

实际应用:

  • 如果上午8:00开始上课,每节课40分钟,课间休息10分钟
  • 第1节课: 8:00 - 8:40
  • 第2节课: 8:50 - 9:30 (课间休息10分钟)
  • 第3节课: 9:40 - 10:20
  • 第4节课: 10:30 - 11:10

你能算出:

  1. 上午4节课一共多少分钟?(40 × 4 = 160分钟)
  2. 160分钟 = 多少小时多少分钟?(2小时40分钟)
  3. 如果下午也是4节课,全天一共多少分钟?(320分钟 = 5小时20分钟)

这些时间计算在我们的课表程序中都会用到!

程序3:带时间信息的课表系统

# 同义词映射表
synonyms = {
    "周一": "周一", "星期一": "周一", "礼拜一": "周一", "周1": "周一", "1": "周一",
    "周二": "周二", "星期二": "周二", "礼拜二": "周二", "周2": "周二", "2": "周二",
    "周三": "周三", "星期三": "周三", "礼拜三": "周三", "周3": "周三", "3": "周三",
    "周四": "周四", "星期四": "周四", "礼拜四": "周四", "周4": "周四", "4": "周四",
    "周五": "周五", "星期五": "周五", "礼拜五": "周五", "周5": "周五", "5": "周五",
    "周六": "周六", "星期六": "周六", "礼拜六": "周六", "周6": "周六", "6": "周六",
    "周日": "周日", "星期日": "周日", "星期天": "周日", "周7": "周日", "7": "周日", "0": "周日",
}

# 上午下午的同义词
time_synonyms = {
    "上午": "上午", "am": "上午", "早上": "上午", "早晨": "上午",
    "下午": "下午", "pm": "下午", "午后": "下午",
}

# 定义课表(嵌套字典: 星期 -> 时间段 -> 课程列表)
schedule = {
    "周一": {
        "上午": ["语文", "数学", "英语"],
        "下午": ["体育", "音乐"]
    },
    "周二": {
        "上午": ["数学", "语文", "英语"],
        "下午": ["科学", "美术"]
    },
    "周三": {
        "上午": ["英语", "数学", "语文"],
        "下午": ["体育", "班会"]
    },
    "周四": {
        "上午": ["音乐", "语文", "数学"],
        "下午": ["美术", "科学"]
    },
    "周五": {
        "上午": ["体育", "英语", "语文"],
        "下午": ["综合实践"]
    },
    "周六": {"上午": [], "下午": []},
    "周日": {"上午": [], "下午": []}
}

print("=" * 40)
print("       欢迎使用课表查询系统")
print("=" * 40)
print("查询方式:")
print("1. 输入 '周一' - 查询周一全天课程")
print("2. 输入 '周一上午' 或 '周一 am' - 查询周上午课程")
print("输入 '退出' 结束程序")
print("=" * 40)

while True:
    user_input = input("\n请输入要查询的: ")

    if user_input == "退出":
        print("谢谢使用,再见!")
        break

    # 尝试解析用户输入
    found = False  # 标记是否找到匹配

    # 先尝试匹配"星期+时间"的组合(如"周一上午")
    for day_syn, day_std in synonyms.items():
        for time_syn, time_std in time_synonyms.items():
            # 检查是否包含"星期+时间"或"时间+星期"
            if day_syn in user_input and time_syn in user_input:
                # 找到了匹配
                courses = schedule[day_std][time_std]
                if courses:
                    print(f"\n{day_std}{time_std}的课程有: {', '.join(courses)}")
                else:
                    print(f"\n{day_std}{time_std}没有课,好好休息!")
                found = True
                break
        if found:
            break

    # 如果没有找到"星期+时间",尝试只匹配"星期"
    if not found:
        for day_syn, day_std in synonyms.items():
            if day_syn in user_input or user_input == day_syn:
                day_schedule = schedule[day_std]
                morning_courses = day_schedule["上午"]
                afternoon_courses = day_schedule["下午"]

                print(f"\n{day_std}的课程安排:")
                print(f"  上午: {', '.join(morning_courses) if morning_courses else '无课'}")
                print(f"  下午: {', '.join(afternoon_courses) if afternoon_courses else '无课'}")
                found = True
                break

    if not found:
        print("\n输入错误,请重新输入!")

运行示例:

请输入要查询的: 周一上午

周一上午的课程有: 语文, 数学, 英语

请输入要查询的: 星期二下午

星期二下午的课程有: 科学, 美术

请输入要查询的: 周三

周三的课程安排:
  上午: 英语, 数学, 语文
  下午: 体育, 班会

请输入要查询的: 礼拜五am

周五上午的课程有: 体育, 英语, 语文

给家长的小贴士

教学重点:

  1. 嵌套字典: schedule[day][time] 先通过星期找到上午/下午的字典,再通过时间找到课程列表
  2. 两层循环: 外层循环遍历所有星期的同义词,内层循环遍历所有时间的同义词
  3. in 操作符: if day_syn in user_input 检查用户输入是否包含某个关键词
  4. break 语句: 找到匹配后跳出循环,避免重复查找
  5. found 标志: 跟踪是否找到匹配,如果没有找到则提示输入错误

数据结构图示:

schedule (字典)
├── "周一" (字典)
│   ├── "上午" → ["语文", "数学", "英语"]
│   └── "下午" → ["体育", "音乐"]
├── "周二" (字典)
│   ├── "上午" → ["数学", "语文", "英语"]
│   └── "下午" → ["科学", "美术"]
└── ...

常见问题:

  • : 为什么不用 if day_syn in user_input and time_syn in user_input:?
  • : 因为用户可能输入“上午周一“或“周一上午“,所以两种顺序都要判断

优化建议:

  • 上面的代码效率不高,因为要遍历所有同义词
  • 可以优化:先提取用户输入中的关键词,再匹配

练习3:支持“第几节课“的查询

任务: 修改上面的程序,支持以下查询:

  • “周一第1节” → 查询周一第1节课
  • “周二第2节” → 查询周二第2节课
🔍 查看答案
# 同义词映射表
synonyms = {
    "周一": "周一", "星期一": "周一", "礼拜一": "周一", "周1": "周一", "1": "周一",
    "周二": "周二", "星期二": "周二", "礼拜二": "周二", "周2": "周二", "2": "周二",
    "周三": "周三", "星期三": "周三", "礼拜三": "周三", "周3": "周三", "3": "周三",
    "周四": "周四", "星期四": "周四", "礼拜四": "周四", "周4": "周四", "4": "周四",
    "周五": "周五", "星期五": "周五", "礼拜五": "周五", "周5": "周五", "5": "周五",
    "周六": "周六", "星期六": "周六", "礼拜六": "周六", "周6": "周六", "6": "周六",
    "周日": "周日", "星期日": "周日", "星期天": "周日", "周7": "周日", "7": "周日", "0": "周日",
}

# 定义课表(按节次存储)
schedule = {
    "周一": ["语文", "数学", "英语", "体育", "音乐"],
    "周二": ["数学", "语文", "英语", "科学", "美术"],
    "周三": ["英语", "数学", "语文", "体育", "班会"],
    "周四": ["音乐", "语文", "数学", "美术", "科学"],
    "周五": ["体育", "英语", "语文", "综合实践", "阅读"],
    "周六": [],
    "周日": []
}

print("=" * 40)
print("       欢迎使用课表查询系统")
print("=" * 40)
print("查询方式:")
print("1. 输入 '周一' - 查询周一全天课程")
print("2. 输入 '周一第1节' 或 '周一1' - 查询周一第1节")
print("输入 '退出' 结束程序")
print("=" * 40)

while True:
    user_input = input("\n请输入要查询的: ")

    if user_input == "退出":
        print("谢谢使用,再见!")
        break

    found = False

    # 先尝试匹配"星期+第几节"
    for day_syn, day_std in synonyms.items():
        # 检查是否包含这个星期
        if day_syn in user_input or user_input == day_syn:
            # 尝试提取"第几节"
            if "第" in user_input and "节" in user_input:
                # 提取数字
                for i in range(1, 10):  # 假设最多9节课
                    if f"第{i}节" in user_input or f"{i}" == user_input.strip()[-1]:
                        courses = schedule[day_std]
                        if i <= len(courses):
                            print(f"\n{day_std}第{i}节课是: {courses[i-1]}")
                        else:
                            print(f"\n{day_std}第{i}节没有课!")
                        found = True
                        break
            else:
                # 只查询星期,显示全天课程
                courses = schedule[day_std]
                if courses:
                    print(f"\n{day_std}的课程有:")
                    for i, course in enumerate(courses, 1):
                        print(f"  第{i}节: {course}")
                else:
                    print(f"\n{day_std}没有课,好好休息!")
                found = True
            break

    if not found:
        print("\n输入错误,请重新输入!")

运行示例:

请输入要查询的: 周一第1节

周一第1节课是: 语文

请输入要查询的: 周二第3节

周二第3节课是: 英语

请输入要查询的: 周三
周三的课程有:
  第1节: 英语
  第2节: 数学
  第3节: 语文
  第4节: 体育
  第5节: 班会

改进说明:

  1. 把课表改成列表,每个元素代表一节课
  2. enumerate(courses, 1) 遍历列表,同时获取节次和课程
  3. courses[i-1] 获取第i节课(索引从0开始,所以是i-1)

练习3.5:时间计算挑战 🧮

现在让我们用编程来练习数学中的时间计算!

任务: 编写一个程序,计算上课的总时长

# 每节课的时间信息(分钟)
class_duration = 40  # 每节课40分钟
break_duration = 10  # 课间休息10分钟

# 每天的课程安排
daily_schedule = {
    "周一": {"上午": 4, "下午": 3},  # 周一上午4节,下午3节
    "周二": {"上午": 4, "下午": 3},
    "周三": {"上午": 4, "下午": 3},
    "周四": {"上午": 4, "下午": 3},
    "周五": {"上午": 4, "下午": 2},
}

print("=" * 40)
print("       课程时间计算器")
print("=" * 40)

# 计算某天的上课总时长
day = input("请输入要计算的星期(如'周一'): ")

if day in daily_schedule:
    morning_classes = daily_schedule[day]["上午"]
    afternoon_classes = daily_schedule[day]["下午"]
    total_classes = morning_classes + afternoon_classes

    # 计算上课总时长(分钟)
    total_minutes = total_classes * class_duration

    # 计算在校总时长(包括课间休息)
    total_breaks = (morning_classes - 1) + (afternoon_classes - 1) + 1  # 上午课间+下午课间+午休
    total_with_breaks = total_minutes + total_breaks * break_duration + 60  # +60分钟午休

    # 转换成小时和分钟
    hours = total_minutes // 60
    minutes = total_minutes % 60

    print(f"\n{day}的课程安排:")
    print(f"  上午: {morning_classes}节课")
    print(f"  下午: {afternoon_classes}节课")
    print(f"  总共: {total_classes}节课")
    print(f"\n上课总时长: {total_minutes}分钟 = {hours}小时{minutes}分钟")
    print(f"在校时长(含课间): {total_with_breaks}分钟")

else:
    print("输入错误,请重新输入!")

运行示例:

请输入要计算的星期(如'周一'): 周一

周一的课程安排:
  上午: 4节课
  下午: 3节课
  总共: 7节课

上课总时长: 280分钟 = 4小时40分钟
在校时长(含课间): 420分钟

🧮 数学练习:

  1. 基础题: 如果每节课45分钟,课间休息10分钟,计算周一在校总时长?

    • 提示: 7节课 × 45分钟 = ?
  2. 进阶题: 一周5天,每天平均6节课,一周总共上多少分钟课?

    • 提示: 5天 × 6节课 × 40分钟 = ?
    • 答案: 1200分钟 = 20小时!
  3. 挑战题: 如果你每天早上7:30到校,下午4:30放学,你在学校多少小时?

    • 提示: 下午4:30 = 16:30
    • 计算: 16:30 - 7:30 = ?
🔍 查看答案
# 答案1: 每节课45分钟
class_duration = 45
total_minutes = 7 * 45  # 315分钟
hours = 315 // 60  # 5小时
minutes = 315 % 60  # 15分钟
print(f"周一上课总时长: {hours}小时{minutes}分钟")  # 5小时15分钟

# 答案2: 一周总时长
weekly_minutes = 5 * 6 * 40  # 1200分钟
weekly_hours = 1200 // 60  # 20小时
print(f"一周上课总时长: {weekly_hours}小时")  # 20小时

# 答案3: 在校时长计算
# 16:30 - 7:30 = 9小时
print("在校时长: 9小时")

第四步:支持日期查询(10月15日是星期几?)

现在我们要实现一个更高级的功能:用户输入“10月15日“,程序自动判断是星期几,然后查询课表!

这需要用到时间库(time库或datetime库)。

程序4:支持日期查询

import datetime  # 导入时间库

# 同义词映射表
synonyms = {
    "周一": "周一", "星期一": "周一", "礼拜一": "周一", "周1": "周一", "1": "周一",
    "周二": "周二", "星期二": "周二", "礼拜二": "周二", "周2": "周二", "2": "周二",
    "周三": "周三", "星期三": "周三", "礼拜三": "周三", "周3": "周三", "3": "周三",
    "周四": "周四", "星期四": "周四", "礼拜四": "周四", "周4": "周四", "4": "周四",
    "周五": "周五", "星期五": "周五", "礼拜五": "周五", "周5": "周五", "5": "周五",
    "周六": "周六", "星期六": "周六", "礼拜六": "周六", "周6": "周六", "6": "周六",
    "周日": "周日", "星期日": "周日", "星期天": "周日", "周7": "周日", "7": "周日", "0": "周日",
}

# 定义课表
schedule = {
    "周一": ["语文", "数学", "英语"],
    "周二": ["数学", "语文", "体育"],
    "周三": ["英语", "科学", "美术"],
    "周四": ["音乐", "数学", "语文"],
    "周五": ["体育", "英语", "班会"],
    "周六": [],
    "周日": []
}

# 定义一个函数:根据日期判断星期几
def get_weekday(date_string):
    """
    根据日期字符串(如"10月15日"或"10-15")判断星期几
    返回"周一"、"周二"等
    """
    # 获取当前年份
    current_year = datetime.datetime.now().year

    # 尝试解析日期
    try:
        # 方式1: "10月15日" → "10-15"
        date_string = date_string.replace("月", "-").replace("日", "").replace(" ", "")

        # 方式2: "10/15"
        if "/" in date_string:
            month, day = date_string.split("/")
        # 方式3: "10-15"
        elif "-" in date_string:
            month, day = date_string.split("-")
        else:
            return None  # 无法解析

        # 转换成整数
        month = int(month)
        day = int(day)

        # 创建日期对象
        date_obj = datetime.date(current_year, month, day)

        # 获取星期几(0=周一, 6=周日)
        weekday = date_obj.weekday()

        # 转换成中文
        weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
        return weekdays[weekday]

    except:
        return None  # 解析失败

print("=" * 40)
print("       欢迎使用课表查询系统")
print("=" * 40)
print("查询方式:")
print("1. 输入 '周一' - 查询周一课程")
print("2. 输入 '10月15日' 或 '10-15' - 自动判断星期几并查询")
print("输入 '退出' 结束程序")
print("=" * 40)

while True:
    user_input = input("\n请输入要查询的: ")

    if user_input == "退出":
        print("谢谢使用,再见!")
        break

    found = False

    # 先尝试匹配星期
    for day_syn, day_std in synonyms.items():
        if day_syn in user_input or user_input == day_syn:
            courses = schedule[day_std]
            if courses:
                print(f"\n{day_std}的课程有: {', '.join(courses)}")
            else:
                print(f"\n{day_std}没有课,好好休息!")
            found = True
            break

    # 如果没有匹配到星期,尝试解析为日期
    if not found:
        weekday = get_weekday(user_input)
        if weekday:
            courses = schedule[weekday]
            print(f"\n{user_input} 是 {weekday}")
            if courses:
                print(f"课程有: {', '.join(courses)}")
            else:
                print(f"今天没有课,好好休息!")
            found = True

    if not found:
        print("\n输入错误,请重新输入!")

运行示例:

请输入要查询的: 10月15日

10月15日 是 周二
课程有: 数学, 语文, 体育

请输入要查询的: 10-20

10-20 是 周日
今天没有课,好好休息!

请输入要查询的: 周五

周五的课程有: 体育, 英语, 班会

给家长的小贴士

教学重点:

  1. datetime: Python内置的时间处理库
  2. datetime.date(year, month, day): 创建一个日期对象
  3. .weekday() 方法: 返回星期几(0=周一, 6=周日)
  4. 字符串操作: .replace() 替换字符,.split() 分割字符串
  5. 异常处理: try-except 捕获错误,避免程序崩溃

🖥️ 计算机知识延伸:

1. 时间戳(Timestamp)

  • 计算机内部用时间戳表示时间:从1970年1月1日0时0分0秒到现在的秒数
  • 例如: 2025年10月15日的时间戳可能是 1728950400
  • 为什么要用时间戳? 方便计算时间差!

2. UTC时间和本地时间

  • UTC时间: 世界标准时间(格林威治时间)
  • 本地时间: 你所在时区的时间(如北京时间 = UTC+8)
  • 计算机通常存储UTC时间,显示时转换成本地时间

3. 时区问题

  • 如果你的用户在不同国家,需要考虑时区差异
  • 例如: 北京时间是晚上8点,伦敦时间是中午12点

家长如何辅导:

  • 🔍 观察生活: 和孩子一起观察生活中的时间表示(钟表、日历、手机时间)
  • 🧮 数学联系: 用时间计算复习小学数学的时间单位换算
  • 🌍 地理联系: 通过时区学习地理知识(不同国家的时差)
  • 💻 技术联系: 了解计算机如何存储和处理时间数据

datetime 库详解:

import datetime

# 获取当前日期
today = datetime.date.today()
print(today)  # 2025-10-15

# 创建指定日期
date_obj = datetime.date(2025, 10, 15)
print(date_obj)  # 2025-10-15

# 获取星期几
weekday = date_obj.weekday()
print(weekday)  # 1 (表示周二)

# 获取年、月、日
print(date_obj.year)   # 2025
print(date_obj.month)  # 10
print(date_obj.day)    # 15

常见问题:

  • : 为什么 weekday() 返回0-6,不是1-7?
  • : 这是计算机的惯例,0表示周一,6表示周日
  • : try-except 是什么?
  • : try尝试执行代码,如果出错就执行except中的代码,避免程序崩溃

扩展思考:

  • 如何支持“明天“、“后天”、“下周三”?(提示:计算日期偏移)
  • 如何跨年查询?(提示:解析年份)

练习4:支持“明天“和“后天“

任务: 修改上面的程序,支持以下查询:

  • “明天” → 查询明天的课程
  • “后天” → 查询后天的课程
  • “昨天” → 查询昨天的课程
🔍 查看答案
import datetime

# 同义词映射表
synonyms = {
    "周一": "周一", "星期一": "周一", "礼拜一": "周一", "周1": "周一", "1": "周一",
    "周二": "周二", "星期二": "周二", "礼拜二": "周二", "周2": "周二", "2": "周二",
    "周三": "周三", "星期三": "周三", "礼拜三": "周三", "周3": "周三", "3": "周三",
    "周四": "周四", "星期四": "周四", "礼拜四": "周四", "周4": "周四", "4": "周四",
    "周五": "周五", "星期五": "周五", "礼拜五": "周五", "周5": "周五", "5": "周五",
    "周六": "周六", "星期六": "周六", "礼拜六": "周六", "周6": "周六", "6": "周六",
    "周日": "周日", "星期日": "周日", "星期天": "周日", "周7": "周日", "7": "周日", "0": "周日",
}

# 定义课表
schedule = {
    "周一": ["语文", "数学", "英语"],
    "周二": ["数学", "语文", "体育"],
    "周三": ["英语", "科学", "美术"],
    "周四": ["音乐", "数学", "语文"],
    "周五": ["体育", "英语", "班会"],
    "周六": [],
    "周日": []
}

# 定义一个函数:根据日期判断星期几
def get_weekday(date_obj):
    """根据日期对象返回星期几"""
    weekday = date_obj.weekday()
    weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
    return weekdays[weekday]

print("=" * 40)
print("       欢迎使用课表查询系统")
print("=" * 40)
print("查询方式:")
print("1. 输入 '周一' - 查询周一课程")
print("2. 输入 '10月15日' - 查询指定日期的课程")
print("3. 输入 '明天'、'后天'、'昨天' - 查询相对日期的课程")
print("输入 '退出' 结束程序")
print("=" * 40)

while True:
    user_input = input("\n请输入要查询的: ")

    if user_input == "退出":
        print("谢谢使用,再见!")
        break

    found = False

    # 先尝试匹配星期
    for day_syn, day_std in synonyms.items():
        if day_syn in user_input or user_input == day_syn:
            courses = schedule[day_std]
            if courses:
                print(f"\n{day_std}的课程有: {', '.join(courses)}")
            else:
                print(f"\n{day_std}没有课,好好休息!")
            found = True
            break

    # 如果没有匹配到星期,尝试特殊关键词
    if not found:
        # 获取今天的日期
        today = datetime.date.today()

        # 判断"明天"、"后天"、"昨天"
        if user_input == "明天":
            tomorrow = today + datetime.timedelta(days=1)
            weekday = get_weekday(tomorrow)
            courses = schedule[weekday]
            print(f"\n明天是 {weekday}")
            print(f"课程有: {', '.join(courses) if courses else '无课'}")
            found = True

        elif user_input == "后天":
            day_after_tomorrow = today + datetime.timedelta(days=2)
            weekday = get_weekday(day_after_tomorrow)
            courses = schedule[weekday]
            print(f"\n后天是 {weekday}")
            print(f"课程有: {', '.join(courses) if courses else '无课'}")
            found = True

        elif user_input == "昨天":
            yesterday = today - datetime.timedelta(days=1)
            weekday = get_weekday(yesterday)
            courses = schedule[weekday]
            print(f"\n昨天是 {weekday}")
            print(f"课程有: {', '.join(courses) if courses else '无课'}")
            found = True

        else:
            # 尝试解析为日期
            try:
                current_year = today.year
                date_string = user_input.replace("月", "-").replace("日", "").replace(" ", "")

                if "/" in date_string:
                    month, day = date_string.split("/")
                elif "-" in date_string:
                    month, day = date_string.split("-")
                else:
                    raise ValueError("无法解析日期")

                month = int(month)
                day = int(day)

                date_obj = datetime.date(current_year, month, day)
                weekday = get_weekday(date_obj)
                courses = schedule[weekday]

                print(f"\n{user_input} 是 {weekday}")
                print(f"课程有: {', '.join(courses) if courses else '无课'}")
                found = True

            except:
                pass  # 解析失败,继续检查其他情况

    if not found:
        print("\n输入错误,请重新输入!")

运行示例:

请输入要查询的: 明天

明天是 周三
课程有: 英语, 科学, 美术

请输入要查询的: 后天

后天是 周四
课程有: 音乐, 数学, 语文

请输入要查询的: 昨天

昨天是 周一
课程有: 语文, 数学, 英语

改进说明:

  1. 使用 datetime.timedelta(days=1) 表示时间差(1天)
  2. today + timedelta(days=1) → 明天
  3. today - timedelta(days=1) → 昨天
  4. today + timedelta(days=2) → 后天

第五步:添加课程、修改课程、删除课程

现在我们的课表系统只能查询,接下来要增加增删改功能!

程序5:完整的课表管理系统

import datetime

# 同义词映射表
synonyms = {
    "周一": "周一", "星期一": "周一", "礼拜一": "周一", "周1": "周一", "1": "周一",
    "周二": "周二", "星期二": "周二", "礼拜二": "周二", "周2": "周二", "2": "周二",
    "周三": "周三", "星期三": "周三", "礼拜三": "周三", "周3": "周三", "3": "周三",
    "周四": "周四", "星期四": "周四", "礼拜四": "周四", "周4": "周四", "4": "周四",
    "周五": "周五", "星期五": "周五", "礼拜五": "周五", "周5": "周五", "5": "周五",
    "周六": "周六", "星期六": "周六", "礼拜六": "周六", "周6": "周六", "6": "周六",
    "周日": "周日", "星期日": "周日", "星期天": "周日", "周7": "周日", "7": "周日", "0": "周日",
}

# 定义课表
schedule = {
    "周一": ["语文", "数学", "英语"],
    "周二": ["数学", "语文", "体育"],
    "周三": ["英语", "科学", "美术"],
    "周四": ["音乐", "数学", "语文"],
    "周五": ["体育", "英语", "班会"],
    "周六": [],
    "周日": []
}

# 定义一个函数:根据用户输入找到星期几
def find_weekday(user_input):
    """根据用户输入返回星期几(标准格式)"""
    for day_syn, day_std in synonyms.items():
        if day_syn in user_input or user_input == day_syn:
            return day_std
    return None  # 没找到

print("=" * 40)
print("       欢迎使用课表查询系统")
print("=" * 40)
print("命令列表:")
print("1. 查询: '周一' 或 '10月15日'")
print("2. 添加: '添加 周一 体育' - 在周一添加体育课")
print("3. 修改: '修改 周一第1节 英语' - 把周一第1节改成英语")
print("4. 删除: '删除 周一第3节' - 删除周一第3节课")
print("5. 显示全部: '显示' 或 '全部'")
print("输入 '退出' 结束程序")
print("=" * 40)

while True:
    user_input = input("\n请输入命令: ").strip()

    if user_input == "退出":
        print("谢谢使用,再见!")
        break

    # ========== 查询功能 ==========
    # 先尝试匹配星期
    weekday = find_weekday(user_input)
    if weekday and not user_input.startswith(("添加", "修改", "删除")):
        courses = schedule[weekday]
        if courses:
            print(f"\n{weekday}的课程有:")
            for i, course in enumerate(courses, 1):
                print(f"  第{i}节: {course}")
        else:
            print(f"\n{weekday}没有课,好好休息!")
        continue

    # ========== 显示全部课表 ==========
    if user_input in ["显示", "全部", "课表"]:
        print("\n" + "=" * 40)
        print("           完整课表")
        print("=" * 40)
        for day in ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]:
            courses = schedule[day]
            if courses:
                print(f"{day}: {', '.join(courses)}")
            else:
                print(f"{day}: 无课")
        print("=" * 40)
        continue

    # ========== 添加课程 ==========
    if user_input.startswith("添加"):
        # 提取信息: "添加 周一 体育"
        parts = user_input[3:].strip().split()  # 去掉"添加",然后按空格分割
        if len(parts) >= 2:
            # 第一部分是星期,第二部分是课程
            weekday_input = parts[0]
            course = parts[1]

            weekday = find_weekday(weekday_input)
            if weekday:
                schedule[weekday].append(course)  # 添加到列表末尾
                print(f"\n✓ 已在{weekday}添加课程: {course}")
            else:
                print("\n✗ 星期输入错误!")
        else:
            print("\n✗ 格式错误! 正确格式: 添加 周一 体育")
        continue

    # ========== 修改课程 ==========
    if user_input.startswith("修改"):
        # 提取信息: "修改 周一第1节 英语"
        content = user_input[3:].strip()  # 去掉"修改"
        found = False

        # 尝试找到星期和节次
        for day_syn, day_std in synonyms.items():
            if day_syn in content:
                # 尝试提取"第X节"
                for i in range(1, 10):
                    if f"第{i}节" in content:
                        # 提取新课程名称
                        # 例如: "周一第1节 英语"
                        course_start = content.find(f"第{i}节") + len(f"第{i}节")
                        new_course = content[course_start:].strip()

                        # 修改课程
                        if i <= len(schedule[day_std]):
                            old_course = schedule[day_std][i-1]
                            schedule[day_std][i-1] = new_course
                            print(f"\n✓ 已把{day_std}第{i}节从'{old_course}'修改为'{new_course}'")
                        else:
                            print(f"\n✗ {day_std}第{i}节不存在!")
                        found = True
                        break
                if found:
                    break
        if not found:
            print("\n✗ 格式错误! 正确格式: 修改 周一第1节 英语")
        continue

    # ========== 删除课程 ==========
    if user_input.startswith("删除"):
        # 提取信息: "删除 周一第3节"
        content = user_input[3:].strip()  # 去掉"删除"
        found = False

        # 尝试找到星期和节次
        for day_syn, day_std in synonyms.items():
            if day_syn in content:
                # 尝试提取"第X节"
                for i in range(1, 10):
                    if f"第{i}节" in content:
                        # 删除课程
                        if i <= len(schedule[day_std]):
                            deleted_course = schedule[day_std].pop(i-1)
                            print(f"\n✓ 已删除{day_std}第{i}节: {deleted_course}")
                        else:
                            print(f"\n✗ {day_std}第{i}节不存在!")
                        found = True
                        break
                if found:
                    break
        if not found:
            print("\n✗ 格式错误! 正确格式: 删除 周一第3节")
        continue

    # 如果都不匹配,提示错误
    print("\n✗ 无法识别的命令,请重新输入!")

运行示例:

请输入命令: 添加 周一 体育

✓ 已在周一添加课程: 体育

请输入命令: 添加 周二 音乐

✓ 已在周二添加课程: 音乐

请输入命令: 修改 周一第1节 英语

✓ 已把周一第1节从'语文'修改为'英语'

请输入命令: 删除 周三第2节

✓ 已删除周三第2节: 科学

请输入命令: 显示

========================================
           完整课表
========================================
周一: 英语, 数学, 英语, 体育
周二: 数学, 语文, 体育, 音乐
周三: 英语, 美术
周四: 音乐, 数学, 语文
周五: 体育, 英语, 班会
周六: 无课
周日: 无课
========================================

给家长的小贴士

教学重点:

  1. .startswith() 方法: 判断字符串是否以某个词开头
  2. .split() 方法: 按空格分割字符串
  3. .append() 方法: 在列表末尾添加元素
  4. .pop(index) 方法: 删除指定索引的元素
  5. continue 语句: 跳过本次循环,进入下一次循环
  6. 字符串切片: user_input[3:] 去掉前3个字符

命令解析技巧:

# "添加 周一 体育"
command = user_input[:2]  # "添加"
content = user_input[2:]    # " 周一 体育"
weekday = content.split()[0]  # "周一"
course = content.split()[1]     # "体育"

常见错误:

  • 错误: 忘记用 .strip() 去掉首尾空格
  • 后果: "添加""添加 " 会被判断为不同的命令
  • 解决: user_input.strip()

练习5:完善增删改功能

任务: 修改上面的程序,增加以下功能:

  1. 清空课程: “清空 周一” → 清空周一的所有课程
  2. 插入课程: “插入 周一第2节 美术” → 在周一第2节位置插入美术课
  3. 课程统计: “统计” → 显示每周共多少节课
🔍 查看答案
# 在上面的程序基础上,增加以下功能:

# ========== 清空课程 ==========
if user_input.startswith("清空"):
    content = user_input[2:].strip()
    weekday = find_weekday(content)
    if weekday:
        count = len(schedule[weekday])
        schedule[weekday] = []  # 清空列表
        print(f"\n✓ 已清空{weekday}的所有课程 (共{count}节)")
    else:
        print("\n✗ 星期输入错误!")
    continue

# ========== 插入课程 ==========
if user_input.startswith("插入"):
    content = user_input[2:].strip()
    found = False

    for day_syn, day_std in synonyms.items():
        if day_syn in content:
            for i in range(1, 10):
                if f"第{i}节" in content:
                    # 提取课程名称
                    course_start = content.find(f"第{i}节") + len(f"第{i}节")
                    new_course = content[course_start:].strip()

                    # 插入课程
                    if i <= len(schedule[day_std]) + 1:
                        schedule[day_std].insert(i-1, new_course)
                        print(f"\n✓ 已在{day_std}第{i}节插入: {new_course}")
                    else:
                        print(f"\n✗ 节次超出范围!")
                    found = True
                    break
            if found:
                break
    if not found:
        print("\n✗ 格式错误! 正确格式: 插入 周一第2节 美术")
    continue

# ========== 课程统计 ==========
if user_input in ["统计", "总数"]:
    total = 0
    print("\n" + "=" * 40)
    print("           课程统计")
    print("=" * 40)
    for day in ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]:
        count = len(schedule[day])
        total += count
        print(f"{day}: {count}节课")
    print("=" * 40)
    print(f"每周共 {total} 节课")
    print("=" * 40)
    continue

运行示例:

请输入命令: 清空 周六

✓ 已清空周六的所有课程 (共0节)

请输入命令: 插入 周二第2节 计算机

✓ 已在周二第2节插入: 计算机

请输入命令: 统计

========================================
           课程统计
========================================
周一: 4节课
周二: 4节课
周三: 2节课
周四: 3节课
周五: 3节课
周六: 0节课
周日: 0节课
========================================
每周共 16 节课
========================================

第六步:保存到文件(数据持久化)

现在程序有个大问题:关闭程序后,所有修改都会丢失!我们需要把课表保存到文件,下次打开时还能用。

💾 计算机小知识:文件系统和硬盘

什么是文件系统?

文件系统是计算机组织和管理文件的方式。你可以把它想象成一个超级大的文件柜:

  • 📁 文件夹(目录):像文件柜的抽屉,用来分类存放文件
  • 📄 文件:像文件柜里的文件,存放具体的内容(文字、图片、程序等)
  • 🔍 路径:文件的地址,告诉计算机去哪里找这个文件

文件存在哪里?

文件保存在计算机的硬盘(Hard Disk)固态硬盘(SSD) 中:

┌─────────────────────────────────┐
│         你的电脑                 │
│  ┌──────────┐  ┌──────────┐    │
│  │  内存    │  │   硬盘   │    │
│  │ (RAM)    │  │ (HDD/SSD)│    │
│  │          │  │          │    │
│  │ 程序运行  │  │ 文件存储  │    │
│  │ 时的数据  │  │  永久数据 │    │
│  └──────────┘  └──────────┘    │
│       ↑             ↑           │
│    断电丢失      断电保留        │
└─────────────────────────────────┘

内存 vs 硬盘的区别:

特点内存(RAM)硬盘(HDD/SSD)
速度非常快慢一些
容量相对小很大
数据保留断电后丢失断电后保留
用途运行程序存储文件
价格较贵较便宜

为什么需要保存到文件?

当我们运行Python程序时:

  • 程序和数据(如课表)都存储在内存
  • 如果不保存到文件,关闭程序后数据就会丢失
  • 保存到文件后,数据存储在硬盘中,下次打开还能用

JSON是什么?

JSON是一种数据格式,就像一个“快递箱“,可以把Python的字典、列表打包,存到文件里:

# Python字典
schedule = {"周一": ["语文", "数学"]}

↓ 转换成JSON字符串

'{"周一": ["语文", "数学"]}'

↓ 保存到硬盘文件

schedule.json (硬盘上的文件)

程序6:支持文件保存和加载

import datetime
import json  # 导入JSON库,用于保存数据

# 同义词映射表
synonyms = {
    "周一": "周一", "星期一": "周一", "礼拜一": "周一", "周1": "周一", "1": "周一",
    "周二": "周二", "星期二": "周二", "礼拜二": "周二", "周2": "周二", "2": "周二",
    "周三": "周三", "星期三": "周三", "礼拜三": "周三", "周3": "周三", "3": "周三",
    "周四": "周四", "星期四": "周四", "礼拜四": "周四", "周4": "周四", "4": "周四",
    "周五": "周五", "星期五": "周五", "礼拜五": "周五", "周5": "周五", "5": "周五",
    "周六": "周六", "星期六": "周六", "礼拜六": "周六", "周6": "周六", "6": "周六",
    "周日": "周日", "星期日": "周日", "星期天": "周日", "周7": "周日", "7": "周日", "0": "周日",
}

# 定义文件名
FILENAME = "schedule.json"

# 定义课表(默认数据)
default_schedule = {
    "周一": ["语文", "数学", "英语"],
    "周二": ["数学", "语文", "体育"],
    "周三": ["英语", "科学", "美术"],
    "周四": ["音乐", "数学", "语文"],
    "周五": ["体育", "英语", "班会"],
    "周六": [],
    "周日": []
}

# 定义一个函数:加载课表
def load_schedule():
    """从文件加载课表,如果文件不存在则使用默认课表"""
    try:
        with open(FILENAME, "r", encoding="utf-8") as f:
            data = f.read()
            schedule = json.loads(data)
            print(f"\n✓ 已从 {FILENAME} 加载课表")
            return schedule
    except FileNotFoundError:
        print(f"\n✗ 文件 {FILENAME} 不存在,使用默认课表")
        return default_schedule.copy()  # 复制一份,避免修改原数据
    except Exception as e:
        print(f"\n✗ 加载失败: {e}")
        return default_schedule.copy()

# 定义一个函数:保存课表
def save_schedule(schedule):
    """把课表保存到文件"""
    try:
        data = json.dumps(schedule, ensure_ascii=False, indent=2)
        with open(FILENAME, "w", encoding="utf-8") as f:
            f.write(data)
        print(f"\n✓ 课表已保存到 {FILENAME}")
    except Exception as e:
        print(f"\n✗ 保存失败: {e}")

# 定义一个函数:根据用户输入找到星期几
def find_weekday(user_input):
    """根据用户输入返回星期几(标准格式)"""
    for day_syn, day_std in synonyms.items():
        if day_syn in user_input or user_input == day_syn:
            return day_std
    return None  # 没找到

# 加载课表
schedule = load_schedule()

print("=" * 40)
print("       欢迎使用课表查询系统")
print("=" * 40)
print("命令列表:")
print("1. 查询: '周一' 或 '10月15日'")
print("2. 添加: '添加 周一 体育' - 在周一添加体育课")
print("3. 修改: '修改 周一第1节 英语' - 把周一第1节改成英语")
print("4. 删除: '删除 周一第3节' - 删除周一第3节课")
print("5. 显示: '显示' 或 '全部'")
print("6. 保存: '保存' - 保存课表到文件")
print("输入 '退出' 结束程序")
print("=" * 40)

while True:
    user_input = input("\n请输入命令: ").strip()

    if user_input == "退出":
        # 退出前自动保存
        save_schedule(schedule)
        print("谢谢使用,再见!")
        break

    # ========== 查询功能 ==========
    weekday = find_weekday(user_input)
    if weekday and not user_input.startswith(("添加", "修改", "删除", "清空", "插入")):
        courses = schedule[weekday]
        if courses:
            print(f"\n{weekday}的课程有:")
            for i, course in enumerate(courses, 1):
                print(f"  第{i}节: {course}")
        else:
            print(f"\n{weekday}没有课,好好休息!")
        continue

    # ========== 显示全部课表 ==========
    if user_input in ["显示", "全部", "课表"]:
        print("\n" + "=" * 40)
        print("           完整课表")
        print("=" * 40)
        for day in ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]:
            courses = schedule[day]
            if courses:
                print(f"{day}: {', '.join(courses)}")
            else:
                print(f"{day}: 无课")
        print("=" * 40)
        continue

    # ========== 保存课表 ==========
    if user_input in ["保存", "save"]:
        save_schedule(schedule)
        continue

    # ========== 添加课程 ==========
    if user_input.startswith("添加"):
        parts = user_input[3:].strip().split()
        if len(parts) >= 2:
            weekday_input = parts[0]
            course = parts[1]

            weekday = find_weekday(weekday_input)
            if weekday:
                schedule[weekday].append(course)
                print(f"\n✓ 已在{weekday}添加课程: {course}")
            else:
                print("\n✗ 星期输入错误!")
        else:
            print("\n✗ 格式错误! 正确格式: 添加 周一 体育")
        continue

    # ========== 修改课程 ==========
    if user_input.startswith("修改"):
        content = user_input[3:].strip()
        found = False

        for day_syn, day_std in synonyms.items():
            if day_syn in content:
                for i in range(1, 10):
                    if f"第{i}节" in content:
                        course_start = content.find(f"第{i}节") + len(f"第{i}节")
                        new_course = content[course_start:].strip()

                        if i <= len(schedule[day_std]):
                            old_course = schedule[day_std][i-1]
                            schedule[day_std][i-1] = new_course
                            print(f"\n✓ 已把{day_std}第{i}节从'{old_course}'修改为'{new_course}'")
                        else:
                            print(f"\n✗ {day_std}第{i}节不存在!")
                        found = True
                        break
                if found:
                    break
        if not found:
            print("\n✗ 格式错误! 正确格式: 修改 周一第1节 英语")
        continue

    # ========== 删除课程 ==========
    if user_input.startswith("删除"):
        content = user_input[3:].strip()
        found = False

        for day_syn, day_std in synonyms.items():
            if day_syn in content:
                for i in range(1, 10):
                    if f"第{i}节" in content:
                        if i <= len(schedule[day_std]):
                            deleted_course = schedule[day_std].pop(i-1)
                            print(f"\n✓ 已删除{day_std}第{i}节: {deleted_course}")
                        else:
                            print(f"\n✗ {day_std}第{i}节不存在!")
                        found = True
                        break
                if found:
                    break
        if not found:
            print("\n✗ 格式错误! 正确格式: 删除 周一第3节")
        continue

    # 如果都不匹配,提示错误
    print("\n✗ 无法识别的命令,请重新输入!")

运行示例:

✓ 已从 schedule.json 加载课表

请输入命令: 添加 周二 计算机

✓ 已在周二添加课程: 计算机

请输入命令: 保存

✓ 课表已保存到 schedule.json

请输入命令: 退出

✓ 课表已保存到 schedule.json
谢谢使用,再见!

保存的文件内容 (schedule.json):

{
  "周一": [
    "语文",
    "数学",
    "英语"
  ],
  "周二": [
    "数学",
    "语文",
    "体育",
    "计算机"
  ],
  "周三": [
    "英语",
    "科学",
    "美术"
  ],
  "周四": [
    "音乐",
    "数学",
    "语文"
  ],
  "周五": [
    "体育",
    "英语",
    "班会"
  ],
  "周六": [],
  "周日": []
}

给家长的小贴士

教学重点:

  1. json: 用于在文件中存储字典和列表
  2. json.dumps(): 把Python对象转换成JSON字符串
  3. json.loads(): 把JSON字符串转换成Python对象
  4. 文件操作: open() 打开文件,f.read() 读取,f.write() 写入
  5. 异常处理: try-except 捕获文件操作错误

💾 计算机知识延伸:

1. 文件的编码(UTF-8)

  • 计算机存储文件时需要把字符转换成二进制(0和1)
  • UTF-8是一种通用的字符编码标准,支持中文、英文、日文等
  • 如果不用UTF-8,中文可能会乱码!

2. 文本文件 vs 二进制文件

  • 文本文件: 用字符编码存储,可以直接阅读(如.txt、.json、.md)
  • 二进制文件: 用二进制存储,需要专门软件打开(如.jpg、.mp3、.exe)

3. 文件路径

  • 相对路径: 相对于当前程序的位置(如schedule.json)
  • 绝对路径: 从根目录开始的完整路径(如C:\Users\张三\schedule.json)

4. 文件读写模式

  • "r": 只读模式(文件必须存在)
  • "w": 写入模式(会覆盖原文件!)
  • "a": 追加模式(在文件末尾添加)
  • "r+": 读写模式

家长如何辅导:

  • 📂 观察文件: 打开schedule.json文件,让孩子看到JSON格式的结构
  • 🔍 对比数据: 修改程序中的课表,保存后观察文件变化
  • 💡 讨论存储: 为什么需要把数据保存到文件?关机后数据还在吗?
  • 🛡️ 安全意识: 讲解为什么“写入模式“会覆盖原文件,培养数据安全意识

扩展活动:

  1. 用记事本打开schedule.json,手动修改课程名称,再运行程序看效果
  2. 删除schedule.json文件,看程序如何处理(会使用默认课表)
  3. schedule.json复制一份,体验“备份“的概念

json 库详解:

import json

# Python字典
data = {
    "name": "小明",
    "age": 10,
    "courses": ["语文", "数学", "英语"]
}

# 转换成JSON字符串
json_string = json.dumps(data, ensure_ascii=False, indent=2)
print(json_string)
# 输出:
# {
#   "name": "小明",
#   "age": 10,
#   "courses": ["语文", "数学", "英语"]
# }

# 从JSON字符串转换回Python字典
data2 = json.loads(json_string)
print(data2["name"])  # 小明

文件操作详解:

# 写入文件
with open("schedule.json", "w", encoding="utf-8") as f:
    f.write(json_string)

# 读取文件
with open("schedule.json", "r", encoding="utf-8") as f:
    content = f.read()
    data = json.loads(content)

常见问题:

  • : 为什么要用 encoding="utf-8"?
  • : 支持中文,避免乱码
  • : with open(...)f = open(); ...; f.close() 有什么区别?
  • : with 会自动关闭文件,更安全

第七步:使用正则表达式(高级功能)

现在我们的程序有个问题:用户必须严格按照格式输入命令,比如:

  • ✅ “添加 周一 体育”
  • ❌ “添加周一体育” (缺少空格)
  • ❌ “在周一添加体育课” (格式不对)

**正则表达式(Regular Expression)**可以帮我们灵活地解析用户输入!

什么是正则表达式?

正则表达式是一种匹配字符串的模式。它可以:

  • ✅ 灵活匹配各种格式的输入
  • ✅ 提取字符串中的关键信息
  • ✅ 验证输入格式是否正确

举个例子:

import re

# 匹配"周一"、"星期一"、"礼拜一"
pattern = r"(周一|星期一|礼拜一)"
text = "今天是星期一"
result = re.search(pattern, text)
if result:
    print("找到匹配:", result.group())  # 星期一

程序7:使用正则表达式解析命令

import datetime
import json
import re  # 导入正则表达式库

# 同义词映射表(保持不变)
synonyms = {
    "周一": "周一", "星期一": "周一", "礼拜一": "周一", "周1": "周一", "1": "周一",
    "周二": "周二", "星期二": "周二", "礼拜二": "周二", "周2": "周二", "2": "周二",
    "周三": "周三", "星期三": "周三", "礼拜三": "周三", "周3": "周三", "3": "周三",
    "周四": "周四", "星期四": "周四", "礼拜四": "周四", "周4": "周四", "4": "周四",
    "周五": "周五", "星期五": "周五", "礼拜五": "周五", "周5": "周五", "5": "周五",
    "周六": "周六", "星期六": "周六", "礼拜六": "周六", "周6": "周六", "6": "周六",
    "周日": "周日", "星期日": "周日", "星期天": "周日", "周7": "周日", "7": "周日", "0": "周日",
}

# 文件名
FILENAME = "schedule.json"

# 默认课表
default_schedule = {
    "周一": ["语文", "数学", "英语"],
    "周二": ["数学", "语文", "体育"],
    "周三": ["英语", "科学", "美术"],
    "周四": ["音乐", "数学", "语文"],
    "周五": ["体育", "英语", "班会"],
    "周六": [],
    "周日": []
}

# 加载和保存函数(保持不变)
def load_schedule():
    try:
        with open(FILENAME, "r", encoding="utf-8") as f:
            data = f.read()
            schedule = json.loads(data)
            print(f"\n✓ 已从 {FILENAME} 加载课表")
            return schedule
    except:
        print(f"\n✓ 使用默认课表")
        return default_schedule.copy()

def save_schedule(schedule):
    try:
        data = json.dumps(schedule, ensure_ascii=False, indent=2)
        with open(FILENAME, "w", encoding="utf-8") as f:
            f.write(data)
        print(f"\n✓ 课表已保存")
    except Exception as e:
        print(f"\n✗ 保存失败: {e}")

# 新函数:使用正则表达式解析命令
def parse_command(user_input):
    """
    使用正则表达式解析用户命令
    返回: (命令类型, 星期, 节次, 课程名称)
    """
    # 定义正则表达式模式
    patterns = {
        "查询": r"(周一|周二|周三|周四|周五|周六|周日|星期一|星期二|星期三|星期四|星期五|星期六|星期日|礼拜一|礼拜二|礼拜三|礼拜四|礼拜五|礼拜六|礼拜日)",
        "添加": r"添加.*?(周一|周二|周三|周四|周五|周六|周日|星期一|星期二|星期三|星期四|星期五|星期六|星期日|礼拜一|礼拜二|礼拜三|礼拜四|礼拜五|礼拜六|礼拜日)\s*(.*)",
        "修改": r"修改.*?(周一|周二|周三|周四|周五|周六|周日|星期一|星期二|星期三|星期四|星期五|星期六|星期日|礼拜一|礼拜二|礼拜三|礼拜四|礼拜五|礼拜六|礼拜日).?第(\d+)节\s*(.*)",
        "删除": r"删除.*?(周一|周二|周三|周四|周五|周六|周日|星期一|星期二|星期三|星期四|星期五|星期六|星期日|礼拜一|礼拜二|礼拜三|礼拜四|礼拜五|礼拜六|礼拜日).?第(\d+)节",
    }

    # 尝试匹配每个模式
    for cmd_type, pattern in patterns.items():
        match = re.search(pattern, user_input)
        if match:
            groups = match.groups()
            if cmd_type == "查询":
                return ("查询", groups[0], None, None)
            elif cmd_type == "添加":
                return ("添加", groups[0], None, groups[1].strip())
            elif cmd_type == "修改":
                return ("修改", groups[0], int(groups[1]), groups[2].strip())
            elif cmd_type == "删除":
                return ("删除", groups[0], int(groups[1]), None)

    return (None, None, None, None)  # 没匹配到

# 加载课表
schedule = load_schedule()

print("=" * 40)
print("       欢迎使用课表查询系统")
print("=" * 40)
print("命令列表(更灵活的输入方式):")
print("1. 查询: '周一' / '今天是周几' / '查询周一'")
print("2. 添加: '添加周一体育' / '在周一添加体育课'")
print("3. 修改: '修改周一第1节英语' / '把周一第1节改成英语'")
print("4. 删除: '删除周一第3节' / '把周一第3节删了'")
print("5. 显示: '显示' / '全部课表'")
print("6. 保存: '保存'")
print("输入 '退出' 结束程序")
print("=" * 40)

while True:
    user_input = input("\n请输入命令: ").strip()

    if user_input == "退出":
        save_schedule(schedule)
        print("谢谢使用,再见!")
        break

    # 显示全部课表
    if user_input in ["显示", "全部", "课表", "全部课表"]:
        print("\n" + "=" * 40)
        print("           完整课表")
        print("=" * 40)
        for day in ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]:
            courses = schedule[day]
            if courses:
                print(f"{day}: {', '.join(courses)}")
            else:
                print(f"{day}: 无课")
        print("=" * 40)
        continue

    # 保存课表
    if user_input in ["保存", "save"]:
        save_schedule(schedule)
        continue

    # 使用正则表达式解析命令
    cmd_type, day_input, period, course = parse_command(user_input)

    # 转换星期成标准格式
    if day_input and day_input in synonyms:
        day_std = synonyms[day_input]
    else:
        day_std = None

    # 执行命令
    if cmd_type == "查询" and day_std:
        courses = schedule[day_std]
        if courses:
            print(f"\n{day_std}的课程有:")
            for i, c in enumerate(courses, 1):
                print(f"  第{i}节: {c}")
        else:
            print(f"\n{day_std}没有课,好好休息!")

    elif cmd_type == "添加" and day_std and course:
        schedule[day_std].append(course)
        print(f"\n✓ 已在{day_std}添加课程: {course}")

    elif cmd_type == "修改" and day_std and period and course:
        if period <= len(schedule[day_std]):
            old_course = schedule[day_std][period-1]
            schedule[day_std][period-1] = course
            print(f"\n✓ 已把{day_std}第{period}节从'{old_course}'修改为'{course}'")
else:
            print(f"\n✗ {day_std}第{period}节不存在!")

    elif cmd_type == "删除" and day_std and period:
        if period <= len(schedule[day_std]):
            deleted_course = schedule[day_std].pop(period-1)
            print(f"\n✓ 已删除{day_std}第{period}节: {deleted_course}")
        else:
            print(f"\n✗ {day_std}第{period}节不存在!")

    else:
        print("\n✗ 无法识别的命令,请重新输入!")

运行示例:

请输入命令: 查询周一

周一的课程有:
  第1节: 语文
  第2节: 数学
  第3节: 英语

请输入命令: 在周二添加计算机课

✓ 已在周二添加课程: 计算机课

请输入命令: 把周三第2节改成体育

✓ 已把周三第2节从'科学'修改为'体育'

请输入命令: 删除周四第3节课

✓ 已删除周四第3节: 语文

请输入命令: 今天是周几
无法识别的命令,请重新输入!

给家长的小贴士

教学重点:

  1. re: Python的正则表达式库
  2. re.search(): 在字符串中搜索模式
  3. .groups(): 获取匹配的分组
  4. 正则表达式语法:
    • .*? 匹配任意字符(非贪婪)
    • (a|b) 匹配a或b
    • \d+ 匹配一个或多个数字
    • \s* 匹配零个或多个空格

正则表达式详解:

import re

# 示例1: 提取星期和课程
pattern = r"添加.*?(周一|周二).*?(.*)"
text = "在周一添加体育课"
match = re.search(pattern, text)
if match:
    day = match.group(1)  # 周一
    course = match.group(2)  # 体育课
    print(day, course)

# 示例2: 提取节次
pattern = r"第(\d+)节"
text = "修改周一第3节英语"
match = re.search(pattern, text)
if match:
    period = int(match.group(1))  # 3
    print(period)

优点:

  • ✅ 用户可以用各种说法表达同一意思
  • ✅ 代码更简洁,不需要很多 if 判断
  • ✅ 易于扩展新的命令格式

缺点:

  • ❌ 正则表达式语法复杂,难以学习
  • ❌ 调试困难

综合项目:完整的智能课表系统

现在让我们把所有功能整合起来,做一个完整的课表系统!

完整功能列表

  1. ✅ 查询课程(支持多种说法)
  2. ✅ 添加/修改/删除课程
  3. ✅ 支持上午/下午区分
  4. ✅ 支持日期查询(10月15日)
  5. ✅ 支持相对日期(明天、后天)
  6. ✅ 自动保存到文件
  7. ✅ 正则表达式解析命令
  8. 🆕 课程提醒功能
  9. 🆕 课程搜索功能
  10. 🆕 数据备份功能

挑战练习

请尝试在现有程序基础上,增加以下功能:

挑战1:课程搜索

目标: 用户输入“搜索体育“,显示所有包含“体育“的课程

💡 提示
if user_input.startswith("搜索"):
    keyword = user_input[3:].strip()
    print(f"\n搜索 '{keyword}':")

    found = False
    for day in ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]:
        courses = schedule[day]
        for i, course in enumerate(courses, 1):
            if keyword in course:
                print(f"  {day}第{i}节: {course}")
                found = True

    if not found:
        print(f"  没找到包含'{keyword}'的课程")

挑战2:课程提醒

目标: 每次查询时,如果今天的课程很多(>5节),提醒用户“今天课程较重,注意休息!“

💡 提示
# 在查询功能中增加:
if len(courses) > 5:
    print(f"\n⚠️ 提醒: 今天课程较多({len(courses)}节),注意休息!")

挑战3:数据备份

目标: 增加“备份“命令,把当前课表复制到 schedule_backup.json

💡 提示
if user_input in ["备份", "backup"]:
    backup_filename = "schedule_backup.json"
    data = json.dumps(schedule, ensure_ascii=False, indent=2)
    with open(backup_filename, "w", encoding="utf-8") as f:
        f.write(data)
    print(f"\n✓ 已备份到 {backup_filename}")

本章小结

核心知识点回顾

  1. 命令行程序: 通过文字命令和计算机交互的程序
  2. 字典应用: 用嵌套字典存储复杂数据(课表)
  3. 同义词映射: 用字典把各种说法统一成标准格式
  4. 时间处理: 使用 datetime 库处理日期和时间
  5. 文件操作: 使用 json 库保存和加载数据
  6. 正则表达式: 灵活解析用户输入
  7. 函数封装: 把重复的代码封装成函数

🖥️ 计算机知识回顾

本章我们学到了重要的计算机体系结构知识:

  1. 命令行历史

    • 早期计算机使用命令行交互(1970-1980年代)
    • 命令行占用CPU和内存资源少,运行速度快
    • 图形界面占用资源多,但更直观易用
  2. 文件系统

    • 文件系统是计算机组织文件的方式
    • 文件夹(目录)用于分类管理文件
    • 路径告诉计算机去哪里找文件
  3. 内存 vs 硬盘

    • 内存(RAM): 速度快,断电后数据丢失,用于运行程序
    • 硬盘(HDD/SSD): 速度较慢,断电后数据保留,用于存储文件
    • 数据需要保存到文件才能持久化
  4. 数据编码

    • UTF-8: 通用字符编码,支持中文等多种语言
    • JSON: 轻量级数据交换格式,易于人阅读和编写
    • 文本文件 vs 二进制文件
  5. 时间表示

    • 时间戳: 计算机用从1970年1月1日到现在的秒数表示时间
    • UTC vs 本地时间: 世界标准时间 vs 你所在时区的时间
    • 时区差异: 不同国家的时间差异

🧮 数学知识回顾

本章用到了重要的数学知识:

  1. 时间单位换算

    • 1小时 = 60分钟
    • 1分钟 = 60秒
    • 时间加减法计算
  2. 四则运算应用

    • 计算上课总时长: 节数 × 分钟数
    • 时间单位转换: 分钟 → 小时和分钟
    • 整除和取余: //%
  3. 实际应用问题

    • 计算在校时长(包括课间)
    • 计算一周总课时
    • 时间段计算(7:30 - 16:30)

能力检查表

编程能力:

  • 能够使用字典存储课表数据
  • 能够处理用户的多种说法(同义词)
  • 能够使用 datetime 库判断星期几
  • 能够使用 json 库保存和加载数据
  • 能够使用正则表达式解析命令
  • 能够设计一个完整的命令行程序

计算机知识:

  • 理解命令行程序的历史和特点
  • 理解文件系统的基本概念
  • 理解内存和硬盘的区别
  • 理解数据编码(UTF-8、JSON)
  • 理解时间戳和时区

数学能力:

  • 能够进行时间单位换算
  • 能够计算上课时长
  • 能够解决实际的时间问题

常见错误和调试

错误类型原因解决方法
KeyError: '周一'字典中不存在这个键in 检查键是否存在
IndexError: list index out of range列表索引超出范围检查 i <= len(list)
FileNotFoundError文件不存在try-except 捕获异常
JSONDecodeErrorJSON格式错误检查JSON格式是否正确
正则表达式不匹配模式书写错误使用在线工具测试正则表达式

调试技巧

  1. 打印中间结果:

    print(f"解析结果: cmd={cmd_type}, day={day_std}, period={period}")
    
  2. 简化问题:

    • 先实现简单版本,再逐步增加功能
    • 每增加一个功能,测试一次
  3. 使用在线工具:

    • 正则表达式测试: https://regex101.com/
    • JSON格式化: https://www.json.cn/

挑战练习

练习1:课程时间表

任务: 在课表中增加每节课的时间,如:

"周一": {
    "1": {"课程": "语文", "时间": "8:00-8:45"},
    "2": {"课程": "数学", "时间": "8:55-9:40"},
    ...
}

练习2:课程备注

任务: 为每节课增加备注字段,如:

"周一": [
    {"课程": "语文", "备注": "带作文本"},
    {"课程": "数学", "备注": "带计算器"},
    ...
]

练习3:多课表管理

任务: 支持多个课表(如上半学期、下半学期),用户可以切换:

命令: 切换到上半学期
命令: 切换到下半学期

练习4:课表分享

任务: 增加“导出“功能,把课表导出为:

  • 文本格式(.txt)
  • Markdown格式(.md)
  • HTML格式(.html)

练习5:图形化界面

任务: 使用 tkinter 库(第16章会学)为课表系统添加图形界面!


下一章预告

本章我们开发了一个功能完整的命令行程序,综合运用了:

  • 字典和列表
  • 条件和循环
  • 文件操作
  • 时间处理
  • 正则表达式

下一章(第15章),我们将学习报表程序,使用 pyecharts 库生成各种图表(柱状图、折线图、饼图),让数据可视化!

准备好进入数据可视化的世界了吗? 🎨📊

第15章 报表程序 - 让数据说话

引言:从数字到图表

想象一下,如果你是一名老师,手里有一份学生的考试成绩单,上面只有密密麻麻的数字:

小明: 语文85 数学92 英语78
小红: 语文90 数学88 英语95
小刚: 语文78 数学85 英语82
...

你能快速看出:

  • 哪个科目最难?
  • 每个学生的强项是什么?
  • 全班的平均成绩如何?

如果把这些数字画成图表,比如柱状图或折线图,答案就会一目了然!这就是数据可视化的作用——把枯燥的数字变成直观的图形,让数据“说话“。

什么是报表程序?

报表程序就是用来收集、处理和展示数据的程序。在我们的日常生活中:

  • 天气预报:用折线图显示未来一周的气温变化
  • 股票软件:用K线图显示股票价格波动
  • 运动APP:用饼图显示你今天的运动时间分配
  • 游戏排行榜:用柱状图显示玩家积分排名

这些程序都有一个共同点:把数据变成图表,让人更容易理解数据背后的信息。

为什么学习报表程序?

学习制作报表程序,你将掌握:

  1. 数据处理能力:学会整理和分析数据
  2. 可视化思维:学会用图形表达信息
  3. 实用工具:能制作出真正有用的程序

比如,你可以:

  • 制作班级成绩分析报告
  • 记录自己的零花钱使用情况
  • 统计家里的电费水费变化
  • 分析自己喜欢的游戏数据

本章学习路线

本章将教你如何使用pyecharts库制作各种图表,同时融入数学统计知识:

第1步: 安装和配置pyecharts库
   ↓
第2步: 复习数学统计知识(平均数、中位数、众数)
   ↓
第3步: 柱状图 - 比较不同类别的数据
   ↓
第4步: 折线图 - 显示数据的变化趋势
   ↓
第5步: 饼图 - 显示各部分占整体的比例
   ↓
第6步: 综合项目 - 制作完整的数据报表

👨‍🏫 给家长的小贴士

  • 数据可视化是现代信息时代的重要技能
  • 图表能培养孩子的抽象思维能力
  • 本章重点融入小学数学统计知识:平均数、中位数、众数、数据分组
  • 建议选择孩子感兴趣的数据(如游戏数据、运动数据)作为练习素材
  • 鼓励孩子用图表解决实际问题(如班级统计、家庭记账)
  • 数学知识点提醒:
    • 平均数 = 总数 ÷ 个数
    • 中位数:数据从大到小排列,中间位置的数
    • 众数:出现次数最多的数
    • 方差:数据波动大小的衡量(本章会简单介绍)

15.1 认识pyecharts库

15.1.1 什么是pyecharts?

pyecharts是一个专门用于生成图表的Python库。它的名字可以拆解为:

  • py: Python的缩写
  • echarts: 百度开源的一个强大图表工具
  • s: 表示这是echarts的Python版本

为什么要使用pyecharts?

  1. 功能强大: 支持几十种图表类型
  2. 美观漂亮: 生成的图表配色和样式都很专业
  3. 易于使用: 只需要几行代码就能生成图表
  4. 交互式: 生成的图表支持鼠标悬停、点击缩放等操作

15.1.2 安装pyecharts

在开始使用pyecharts之前,需要先安装它。还记得我们在第13章学过的pip工具吗?

步骤1: 打开命令行窗口

Windows系统:

  1. 按Win+R键
  2. 输入cmd
  3. 按回车键

Mac系统:

  1. 按Command+空格键
  2. 输入“终端“
  3. 按回车键

步骤2: 安装pyecharts

在命令行窗口中输入以下命令:

pip install pyecharts

你会看到类似这样的输出:

Collecting pyecharts
  Downloading pyecharts-2.0.4-py3-none-any.whl (...)
Installing collected packages: pyecharts
Successfully installed pyecharts-2.0.4

步骤3: 验证安装

安装完成后,在Python中输入以下代码测试:

from pyecharts.charts import Bar
print("pyecharts安装成功!")

如果输出“pyecharts安装成功!“说明安装成功了!

步骤4: (可选)安装图片快照功能

如果你想直接把图表保存为图片格式,需要安装额外的插件:

pip install pyecharts_snapshot

这个功能可以把HTML格式的图表转换为PNG或JPG图片。

给家长的小贴士

  • pip安装失败: 可能是因为网络问题,可以尝试使用国内镜像源:pip install pyecharts -i https://pypi.tuna.tsinghua.edu.cn/simple
  • 权限问题: 如果提示权限不足,在Windows上以管理员身份运行命令行,Mac上使用sudo
  • 版本问题: pyecharts有v1和v2两个版本,本书使用v2版本
  • 离线安装: 如果网络不好,可以下载whl文件后本地安装

常见问题解答

Q: 为什么要安装这么大的库? A: pyecharts包含了大量的图表模板和样式文件,所以比较大。但这些文件让我们能做出更专业的图表。

Q: 安装失败怎么办? A: 1) 检查网络连接 2) 尝试升级pip:python -m pip install --upgrade pip 3) 使用国内镜像源

Q: 每次使用都要重新安装吗? A: 不需要!安装一次后,以后在任何Python程序中都可以使用。

15.1.3 pyecharts的基本使用流程

使用pyecharts生成图表一般分为5个步骤:

# 步骤1: 导入需要的图表类
from pyecharts.charts import Bar

# 步骤2: 创建图表对象
bar = Bar()

# 步骤3: 添加数据
bar.add_xaxis(["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"])
bar.add_yaxis("商家A", [5, 20, 36, 10, 10, 100])

# 步骤4: 设置全局选项(可选)
bar.set_global_opts(title_opts={"text": "我的第一个图表"})

# 步骤5: 渲染生成HTML文件
bar.render("my_first_chart.html")

代码解析:

  1. 导入: 从pyecharts.charts导入你需要的图表类(如Bar、Line、Pie)
  2. 创建: 创建一个图表对象
  3. 数据: 用add_xaxis()添加x轴数据,用add_yaxis()添加y轴数据
  4. 选项: 设置标题、图例、工具箱等配置
  5. 渲染: 生成一个HTML文件,用浏览器打开就能看到图表

运行结果: 程序运行后,会在当前目录下生成my_first_chart.html文件。双击这个文件,浏览器会打开它,你就能看到一个漂亮的柱状图了!

给家长的小贴士

  • HTML文件: 生成的图表是网页格式(HTML),不需要额外安装软件就能打开
  • 代码结构: 强调“导入-创建-添加数据-设置选项-渲染“这5步固定流程
  • 逐步演示: 建议先让孩子看完整的运行效果,激发学习兴趣
  • 文件位置: 提醒孩子注意HTML文件的保存位置,方便查找

实践练习1:修改数据

任务: 将上面的代码复制下来,修改数据:

  • x轴改成你喜欢的6种水果
  • y轴改成每种水果的价格(元)
  • 标题改成“水果价格表“

提示:

bar.add_xaxis(["苹果", "香蕉", "橙子", "草莓", "葡萄", "西瓜"])
bar.add_yaxis("价格", [8, 3, 6, 15, 12, 5])

答案:

点击查看完整代码
from pyecharts.charts import Bar

# 创建图表对象
bar = Bar()

# 添加数据
bar.add_xaxis(["苹果", "香蕉", "橙子", "草莓", "葡萄", "西瓜"])
bar.add_yaxis("价格(元)", [8, 3, 6, 15, 12, 5])

# 设置标题
bar.set_global_opts(title_opts={"text": "水果价格表"})

# 生成图表
bar.render("fruit_price.html")

运行后打开fruit_price.html,你就能看到水果价格的柱状图了!


15.2 数学统计知识复习 📊

在开始制作图表之前,让我们先复习一些数学统计知识。这些知识会在制作图表时经常用到!

15.2.1 平均数(Average)

什么是平均数?

平均数是一组数据的“代表值“,它能反映这组数据的整体水平

🍎 生活中的例子: 假设你有5个苹果,分别重: 150克、160克、140克、155克、145克。

计算平均数的步骤:

# 步骤1: 把所有数据加起来
total = 150 + 160 + 140 + 155 + 145
print("总重量:", total)  # 输出: 750克

# 步骤2: 除以数据的个数
average = total / 5
print("平均重量:", average)  # 输出: 150克

用Python计算平均数:

# 一组数据
scores = [85, 90, 78, 92, 88]

# 方法1: 使用sum()和len()
average = sum(scores) / len(scores)
print(f"平均分: {average}")

# 方法2: 用循环计算
total = 0
for score in scores:
    total += score
average = total / len(scores)
print(f"平均分: {average}")

平均数的数学意义:

  • 如果每个人的成绩都等于平均分,那么总分不会变
  • 平均数是“公平“的体现——每个人贡献相同

👨‍🏫 给家长的小贴士

  • 平均数 vs 总数: 问孩子“为什么有了总数还要算平均数?“(因为人数不同时,总数不能直接比较)
  • 生活中的平均数: 平均气温、平均身高、平均速度、平均价格
  • 计算技巧: 先估算,再精确计算。比如[85, 90, 78, 92, 88],可以估算“大约85左右“
  • 错误认知: 平均数不一定是数据中的某个值(比如[1, 2, 10],平均数是4.33,不在原数据中)

15.2.2 中位数(Median)

什么是中位数?

中位数是把数据按大小排列后,位于中间位置的那个数。

🏃 生活中的例子: 跑步比赛,5个人的成绩是:

  • 3分20秒、3分15秒、3分25秒、3分18秒、3分22秒

找中位数的步骤:

# 步骤1: 把数据从小到大排列
times = [3*60+15, 3*60+18, 3*60+20, 3*60+22, 3*60+25]  # 转换为秒
print("排序前:", times)

# 使用Python排序
times_sorted = sorted(times)
print("排序后:", times_sorted)  # [195, 198, 200, 202, 205]

# 步骤2: 找中间位置的数据(第3个)
median_index = len(times_sorted) // 2  # 5//2 = 2(索引从0开始)
median = times_sorted[median_index]
print(f"中位数: {median}秒,即{median//60}分{median%60}秒")

中位数的特点:

  • 数据个数是奇数时,中位数就是中间的那个数
  • 数据个数是偶数时,中位数是中间两个数的平均数

偶数个数据的例子:

# 6个数据
scores = [85, 90, 78, 92, 88, 95]

# 排序
scores_sorted = sorted(scores)  # [78, 85, 88, 90, 92, 95]

# 中间两个数: 第3个(88)和第4个(90)
middle1 = scores_sorted[2]   # 第3个,索引为2
middle2 = scores_sorted[3]   # 第4个,索引为3

median = (middle1 + middle2) / 2
print(f"中位数: {median}")  # 输出: 89.0

中位数的数学意义:

  • 中位数不受极端值(太大或太小的数)的影响
  • 比如工资统计,如果有一个人工资特别高,平均数会失真,但中位数能反映真实情况

👨‍🏫 给家长的小贴士

  • 平均数 vs 中位数: 举例说明差异
    • 班级成绩: [60, 85, 88, 90, 92],平均数=83,中位数=88(接近)
    • 班级成绩: [10, 85, 88, 90, 92],平均数=73,中位数=88(差异大)
  • 什么时候用中位数: 当有极端值(异常大或异常小的数据)时
  • 排序技能: 教孩子手动排序的方法(冒泡排序的简化版)
  • 找位置: 奇数个数据的位置 = (总数+1)÷2,偶数个数据取中间两个的平均

15.2.3 众数(Mode)

什么是众数?

众数是一组数据中出现次数最多的数。

🎨 生活中的例子: 班级同学最喜欢的颜色统计:

  • 红色:5人、蓝色:8人、绿色:3人、黄色:8人、紫色:2人
# 数据
colors = ["红", "蓝", "绿", "黄", "紫"]
counts = [5, 8, 3, 8, 2]

# 找最大值
max_count = max(counts)
print(f"最多人选的人数: {max_count}")

# 找最大值的位置
max_index = counts.index(max_count)
mode_color = colors[max_index]
print(f"众数(最受欢迎的颜色): {mode_color}")

多个众数的情况:

# 数据: [1, 2, 2, 3, 3, 4]
# 2出现2次,3也出现2次
# 这组数据有"双众数": 2和3

# 统计每个数出现的次数
from collections import Counter

data = [1, 2, 2, 3, 3, 4]
count_result = Counter(data)
print("统计结果:", count_result)  # Counter({2: 2, 3: 2, 1: 1, 4: 1})

# 找出现最多的次数
max_count = max(count_result.values())

# 找所有众数
modes = [num for num, count in count_result.items() if count == max_count]
print(f"众数: {modes}")  # 输出: [2, 3]

众数的特点:

  • 一组数据可能没有众数(所有数都只出现1次)
  • 一组数据可能有多个众数
  • 众数一定是数据中的某个值(这一点和平均数不同!)

👨‍🏫 给家长的小贴士

  • 众数 vs 平均数: 众数关注“最多“,平均数关注“整体“
  • 投票选举: 众数就像民主投票,得票最多的当选
  • 实际应用:
    • 商店进货哪种商品(卖得最好的)
    • 鞋厂生产哪种尺码(穿的人最多的)
  • 编程思维: 用字典统计频次是常见的数据处理方法

15.2.4 数据分组(区间统计)

什么是数据分组?

当数据很多时,我们不会逐个统计,而是把数据分成几个区间(范围),统计每个区间有多少个数据。

📊 生活中的例子:

统计班级50个学生的考试成绩:

  • 100分: 3人
  • 90-99分: 12人
  • 80-89分: 20人
  • 70-79分: 10人
  • 60-69分: 4人
  • 60分以下: 1人

用Python实现数据分组:

# 原始成绩数据
scores = [95, 87, 92, 78, 85, 90, 88, 92, 85, 76,
          82, 89, 91, 85, 88, 90, 87, 85, 92, 88]

# 定义分数段
ranges = ["优秀(90-100)", "良好(80-89)", "及格(60-79)", "不及格(0-59)"]
counts = [0, 0, 0, 0]

# 统计每个分数段的人数
for score in scores:
    if score >= 90:
        counts[0] += 1
    elif score >= 80:
        counts[1] += 1
    elif score >= 60:
        counts[2] += 1
    else:
        counts[3] += 1

print("分数段分布:")
for i in range(len(ranges)):
    print(f"{ranges[i]}: {counts[i]}人")

数据分组的数学意义:

  • 简化数据,让数据更容易理解
  • 保留数据的整体趋势,同时忽略细节
  • 方便制作统计图表(直方图、饼图)

分组的技巧:

  1. 等距分组: 每个区间的长度相同(如0-10, 10-20, 20-30)
  2. 有意义分组: 根据实际意义划分(如成绩的优、良、及格)
  3. 边界明确: 明确区间的起点和终点(包括端点吗?)

👨‍🏫 给家长的小贴士

  • 为什么分组: 问孩子“50个人的成绩,逐个柱状图会很拥挤,怎么办?“(引出分组)
  • 选择区间: 举例说明区间太大或太小的问题
    • 太大: [0-100]只有一个区间,没意义
    • 太小: [0-1, 1-2, 2-3…]太细碎
    • 合适: [0-59, 60-69, 70-79, 80-89, 90-100]
  • 包含边界: 90分属于哪个区间?需要约定(通常“左闭右开“[90,100)或“左开右闭“(89,100])
  • 图表对应: 分组数据适合做饼图,显示各部分占比

15.2.5 综合练习:数据分析

现在让我们用刚才学到的统计知识,分析一组真实数据!

📚 案例分析:班级阅读情况统计

# 班级同学这学期阅读的书籍数量
books_read = [5, 8, 12, 6, 7, 10, 9, 8, 15, 6,
              7, 8, 9, 11, 7, 8, 10, 12, 8, 9]

# 1. 计算平均数
average = sum(books_read) / len(books_read)
print(f"平均每人读{average:.1f}本书")

# 2. 找中位数
sorted_books = sorted(books_read)
n = len(sorted_books)
if n % 2 == 1:  # 奇数个
    median = sorted_books[n // 2]
else:  # 偶数个
    median = (sorted_books[n // 2 - 1] + sorted_books[n // 2]) / 2
print(f"中位数: {median}本")

# 3. 找众数
from collections import Counter
count_result = Counter(books_read)
max_count = max(count_result.values())
modes = [num for num, count in count_result.items() if count == max_count]
print(f"众数: {modes}本")

# 4. 数据分组
ranges = ["0-5本", "6-10本", "11-15本", "16本以上"]
counts = [0, 0, 0, 0]
for books in books_read:
    if books <= 5:
        counts[0] += 1
    elif books <= 10:
        counts[1] += 1
    elif books <= 15:
        counts[2] += 1
    else:
        counts[3] += 1

print("\n阅读量分组统计:")
for i in range(len(ranges)):
    percentage = counts[i] / len(books_read) * 100
    print(f"{ranges[i]}: {counts[i]}人,占{percentage:.1f}%")

运行结果:

平均每人读8.7本书
中位数: 8.0本
众数: [8]本

阅读量分组统计:
0-5本: 1人,占5.0%
6-10本: 15人,占75.0%
11-15本: 4人,占20.0%
16本以上: 0人,占0.0%

数据分析结论:

  • 大部分同学(75%)阅读量在6-10本之间
  • 平均数(8.7本)和中位数(8本)接近,说明数据分布比较均匀
  • 众数是8本,说明阅读8本的同学最多
  • 全班没有一个人阅读超过15本,但也没有一个人阅读少于6本(除了1人)

👨‍🏫 给家长的小贴士

  • 数据思维: 教孩子从数据中“读故事“,而不是只看数字
  • 多角度分析: 平均数、中位数、众数各有用处,综合分析更全面
  • 批判性思维: 问孩子“如果你是老师,看到这个数据会怎么做?“(比如:组织读书分享会)
  • 现实应用: 鼓励孩子用这套方法分析自己的数据(游戏时长、学习时间等)
  • 编程价值: 强调Python让数据分析变得简单高效,手算20个数据要很久,Python只需要几秒

🎯 实践练习2:你自己的数据分析

任务: 收集一组你感兴趣的数据,并用Python分析:

数据主题建议:

  1. 游戏数据: 最近10次游戏的得分/等级
  2. 运动数据: 最近2周每天的运动时长
  3. 学习数据: 最近10次作业的分数
  4. 创意数据: 其他你感兴趣的数据

要求:

  • 至少10个数据点
  • 计算平均数、中位数、众数
  • 进行数据分组(至少3个区间)
  • 写出分析结论(至少3条)

答案示例:

点击查看参考答案
# 示例:游戏得分分析
scores = [85, 92, 78, 88, 95, 82, 90, 87, 91, 89]

# 平均数
avg = sum(scores) / len(scores)
print(f"平均分: {avg:.1f}")

# 中位数
sorted_scores = sorted(scores)
median = (sorted_scores[4] + sorted_scores[5]) / 2
print(f"中位数: {median}")

# 众数
from collections import Counter
count_result = Counter(scores)
max_count = max(count_result.values())
modes = [s for s, c in count_result.items() if c == max_count]
print(f"众数: {modes}")

# 数据分组
ranges = ["优秀(90-100)", "良好(80-89)", "及格(60-79)", "不及格(0-59)"]
counts = [0, 0, 0, 0]
for score in scores:
    if score >= 90:
        counts[0] += 1
    elif score >= 80:
        counts[1] += 1
    elif score >= 60:
        counts[2] += 1
    else:
        counts[3] += 1

print("\n分数段分布:")
for i in range(len(ranges)):
    print(f"{ranges[i]}: {counts[i]}人")

15.3 柱状图 - 比较大小的高手

15.3.1 柱状图的应用场景

**柱状图(Bar Chart)**是最常见的图表类型之一,它用柱子的高度来表示数据的大小。

柱状图适合用来:

  • 比较不同类别: 比较不同产品的销量
  • 排名: 显示班级成绩排名
  • 统计: 统计每个月的零花钱支出

生活中的柱状图例子:

  • 奥运会奖牌榜
  • 游戏英雄战斗力对比
  • 各城市人口数量对比

15.3.2 创建一个简单的柱状图

让我们从最简单的例子开始——制作一个班级成绩的柱状图:

from pyecharts.charts import Bar

# 创建柱状图
bar = Bar()

# 添加x轴数据(学生姓名)
bar.add_xaxis(["小明", "小红", "小刚", "小丽", "小华"])

# 添加y轴数据(语文成绩)
bar.add_yaxis("语文成绩", [85, 90, 78, 92, 88])

# 设置标题
bar.set_global_opts(title_opts={"text": "班级语文成绩"})

# 生成HTML文件
bar.render("class_scores.html")

运行结果: 程序会生成class_scores.html文件,用浏览器打开后,你会看到5个不同高度的柱子,每个柱子代表一个学生的语文成绩。

15.3.3 添加多个系列的数据

有时候我们需要对比多组数据。比如,想同时显示每个学生的语文、数学、英语成绩:

from pyecharts.charts import Bar

bar = Bar()

# 添加x轴(学生姓名)
bar.add_xaxis(["小明", "小红", "小刚", "小丽", "小华"])

# 添加多组数据
bar.add_yaxis("语文", [85, 90, 78, 92, 88])
bar.add_yaxis("数学", [92, 88, 85, 95, 90])
bar.add_yaxis("英语", [78, 95, 82, 88, 91])

# 设置标题
bar.set_global_opts(title_opts={"text": "班级成绩表"})

# 生成图表
bar.render("class_scores_all.html")

图表解读:

  • 每个学生会有3根柱子,分别代表语文、数学、英语成绩
  • 不同颜色的柱子代表不同的科目
  • 图表右上角会显示图例(语文、数学、英语),点击可以隐藏/显示对应的数据

给家长的小贴士

  • 颜色区分: 帮助孩子理解不同颜色代表不同数据系列
  • 图例功能: 演示点击图例可以隐藏/显示数据,这是pyecharts的交互功能
  • 数据对应: 确保x轴和y轴的数据数量一致,否则会报错
  • 中文字符: pyecharts完全支持中文,不会出现乱码问题

实践练习2:最喜欢的运动统计

任务: 调查班级同学最喜欢的运动,制作柱状图:

  • x轴: 篮球、足球、羽毛球、乒乓球、跑步、游泳
  • y轴: 喜欢该项运动的人数(假设为: 15, 12, 8, 10, 6, 9)
  • 标题: “班级运动喜好调查”

要求:

  1. 添加y轴名称:“人数”
  2. 添加数据标签显示每个柱子上的具体数值

答案:

点击查看完整代码
from pyecharts.charts import Bar
from pyecharts import options as opts

bar = Bar()

# 添加数据
bar.add_xaxis(["篮球", "足球", "羽毛球", "乒乓球", "跑步", "游泳"])
bar.add_yaxis(
    series_name="人数",
    yaxis_data=[15, 12, 8, 10, 6, 9],
    label_opts=opts.LabelOpts(is_show=True)  # 显示数据标签
)

# 设置全局选项
bar.set_global_opts(
    title_opts={"text": "班级运动喜好调查"},
    yaxis_opts=opts.AxisOpts(name="人数"),
    xaxis_opts=opts.AxisOpts(name="运动项目")
)

# 生成图表
bar.render("sports_survey.html")

运行后,每个柱子上会显示具体人数,方便读取数据!

15.3.4 柱状图的高级设置

1. 水平柱状图

默认的柱状图是垂直的,柱子从下往上。有时我们需要水平的柱状图,柱子从左往右:

from pyecharts.charts import Bar
from pyecharts import options as opts

bar = Bar()

# 添加数据(反转x和y轴的添加顺序)
bar.add_xaxis(["篮球", "足球", "羽毛球", "乒乓球", "跑步", "游泳"])
bar.add_yaxis("人数", [15, 12, 8, 10, 6, 9])

# 设置全局选项
bar.set_global_opts(
    title_opts={"text": "班级运动喜好调查"},
    yaxis_opts=opts.AxisOpts(name="人数"),
    xaxis_opts=opts.AxisOpts(name="运动项目")
)

# 反转x轴和y轴
bar.reversal_axis()

# 生成图表
bar.render("horizontal_bar.html")

使用场景: 水平柱状图适合类别名称很长的情况(如“数学竞赛成绩“、“英语演讲比赛“等),避免文字重叠。

2. 堆叠柱状图

堆叠柱状图可以让多组数据叠加显示:

from pyecharts.charts import Bar
from pyecharts import options as opts
from pyecharts.globals import ThemeType

# 使用主题
bar = Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))

bar.add_xaxis(["第一季度", "第二季度", "第三季度", "第四季度"])
bar.add_yaxis("电子产品", [10, 20, 30, 40], stack="stack1")
bar.add_yaxis("服装", [15, 25, 20, 35], stack="stack1")
bar.add_yaxis("食品", [20, 15, 25, 30], stack="stack1")

bar.set_global_opts(
    title_opts={"text": "季度销售额"},
    yaxis_opts=opts.AxisOpts(name="销售额(万元)"),
    xaxis_opts=opts.AxisOpts(name="季度"),
    toolbox_opts=opts.ToolboxOpts(is_show=True)  # 显示工具栏
)

bar.render("stacked_bar.html")

使用场景: 堆叠柱状图适合显示部分与整体的关系,比如每天不同类别商品的销售总额。

给家长的小贴士

  • 主题样式: pyecharts提供了多个主题(LIGHT, DARK, ROMA等),可以尝试不同风格
  • 工具栏: toolbox_opts会显示下载图片、数据视图等功能,很实用
  • 堆叠概念: 用积木叠高高来类比堆叠柱状图,孩子更容易理解
  • 代码复用: 鼓励孩子把常用配置保存起来,下次直接使用

15.3.5 柱状图的常见问题

问题1: 中文显示乱码

虽然pyecharts默认支持中文,但如果你的Python文件编码不是UTF-8,可能会出现乱码。

解决方法: 在Python文件的第一行添加:

# -*- coding: utf-8 -*-

问题2: 柱子太多显示不下

当x轴的类别太多时,柱子会挤在一起。

解决方法:

bar.set_global_opts(
    xaxis_opts=opts.AxisOpts(
        axislabel_opts=opts.LabelOpts(rotate=45)  # 旋转标签45度
    )
)

问题3: 数值差异太大

如果一组数据是[5, 8, 10],另一组是[1000, 2000, 3000],数值小的柱子几乎看不见。

解决方法: 使用双y轴(在高级应用中会学习)或调整数据比例。

实践练习3:比较自己和偶像的数据

任务: 制作一个柱状图,对比你和偶像的数据(可以是游戏、运动、学习等):

  • 要求:至少3个对比项目(如:身高、体重、年龄,或游戏等级、胜率、场次)
  • 使用水平柱状图
  • 添加标题和数据标签

示例数据:

me = [165, 50, 12]  # 身高cm, 体重kg, 年龄
idol = [178, 65, 25]
items = ["身高(cm)", "体重(kg)", "年龄"]

答案:

点击查看完整代码
from pyecharts.charts import Bar
from pyecharts import options as opts

# 你的数据
me = [165, 50, 12]
idol = [178, 65, 25]
items = ["身高(cm)", "体重(kg)", "年龄"]

bar = Bar()

bar.add_xaxis(items)
bar.add_yaxis("我", me, label_opts=opts.LabelOpts(is_show=True))
bar.add_yaxis("偶像", idol, label_opts=opts.LabelOpts(is_show=True))

bar.set_global_opts(
    title_opts={"text": "我和偶像的对比"}
)

bar.reversal_axis()
bar.render("me_vs_idol.html")

15.4 折线图 - 追踪变化趋势

15.4.1 折线图的应用场景

**折线图(Line Chart)**用线段连接各个数据点,显示数据随时间或其他因素的变化趋势。

折线图适合用来:

  • 显示趋势: 比如一年的气温变化
  • 对比变化: 比较两个学生的成绩进步情况
  • 预测未来: 根据历史数据预测趋势

生活中的折线图例子:

  • 天气预报的气温曲线
  • 新冠疫情的数据曲线
  • 股票价格的K线图

15.4.2 创建一个简单的折线图

让我们制作一个显示一周气温变化的折线图:

from pyecharts.charts import Line

# 创建折线图
line = Line()

# 添加x轴数据(星期)
line.add_xaxis(["周一", "周二", "周三", "周四", "周五", "周六", "周日"])

# 添加y轴数据(最高气温)
line.add_yaxis("最高气温(°C)", [22, 24, 26, 25, 23, 28, 30])

# 设置标题
line.set_global_opts(title_opts={"text": "一周气温变化"})

# 生成图表
line.render("temperature_line.html")

图表解读:

  • x轴表示星期
  • y轴表示温度
  • 折线显示温度的变化趋势
  • 可以看出哪天最热,哪天最凉快

15.4.3 添加多个数据系列

折线图特别适合对比多个数据系列的变化趋势:

from pyecharts.charts import Line

line = Line()

# 添加x轴
line.add_xaxis(["周一", "周二", "周三", "周四", "周五", "周六", "周日"])

# 添加多组数据
line.add_yaxis("最高气温(°C)", [22, 24, 26, 25, 23, 28, 30])
line.add_yaxis("最低气温(°C)", [15, 16, 18, 17, 14, 19, 20])

# 设置标题
line.set_global_opts(title_opts={"text": "一周气温变化"})

# 生成图表
line.render("temperature_double_line.html")

图表解读:

  • 两条折线分别代表最高气温和最低气温
  • 可以看出每天的温差(两条线之间的距离)
  • 可以看出气温的总体变化趋势

给家长的小贴士

  • 趋势解读: 教孩子如何从折线图中读取信息(上升、下降、波动)
  • 数据对比: 多条折线对比时,引导孩子找出规律(如:A一直比B高,A和B呈相反变化)
  • 实际应用: 鼓励孩子记录自己的数据(如每天的学习时间),用折线图分析
  • 数据收集: 这个过程还能培养孩子的数据收集习惯

实践练习4:我的成绩进步曲线

任务: 制作一个折线图,显示你最近5次考试的成绩变化:

  • x轴: 第1次、第2次、第3次、第4次、第5次
  • y轴: 语文和数学的成绩
  • 添加数据标记点

示例数据:

chinese = [75, 78, 82, 85, 88]
math = [80, 79, 83, 87, 90]
exams = ["第1次", "第2次", "第3次", "第4次", "第5次"]

答案:

点击查看完整代码
from pyecharts.charts import Line
from pyecharts import options as opts

# 成绩数据
chinese = [75, 78, 82, 85, 88]
math = [80, 79, 83, 87, 90]
exams = ["第1次", "第2次", "第3次", "第4次", "第5次"]

line = Line()

line.add_xaxis(exams)
line.add_yaxis(
    "语文",
    chinese,
    markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_="max")]),
    markline_opts=opts.MarkLineOpts(data=[opts.MarkLineItem(type_="average")])
)
line.add_yaxis("数学", math)

line.set_global_opts(
    title_opts={"text": "我的成绩进步曲线"},
    yaxis_opts=opts.AxisOpts(name="分数"),
    xaxis_opts=opts.AxisOpts(name="考试次数")
)

line.render("my_scores.html")

运行后,你会看到两条折线,语文的最高分会自动标记,平均分也会显示!

15.4.4 平滑曲线和面积图

1. 平滑曲线

默认的折线图是直线连接各个点。设置is_smooth=True可以让线条变得平滑:

from pyecharts.charts import Line

line = Line()

line.add_xaxis(["1月", "2月", "3月", "4月", "5月", "6月"])
line.add_yaxis("销售额(万元)", [10, 15, 13, 18, 20, 25], is_smooth=True)

line.set_global_opts(title_opts={"text": "上半年销售额"})

line.render("smooth_line.html")

效果: 折线变成了平滑的曲线,看起来更美观,适合表示连续变化的数据(如气温、速度等)。

2. 面积图

面积图是在折线图下方填充颜色,强调数据的“量“:

from pyecharts.charts import Line
from pyecharts import options as opts

line = Line()

line.add_xaxis(["周一", "周二", "周三", "周四", "周五", "周六", "周日"])
line.add_yaxis(
    "学习时间(小时)",
    [2, 2.5, 3, 2, 3.5, 4, 3],
    areastyle_opts=opts.AreaStyleOpts(opacity=0.5)  # 填充区域,透明度0.5
)

line.set_global_opts(title_opts={"text": "一周学习时间统计"})

line.render("area_line.html")

使用场景: 面积图适合强调累积量或总量,如每天的学习时间、每月的零花钱支出。

给家长的小贴士

  • 平滑 vs 直线: 平滑曲线看起来更美观,但不适合强调精确的数据点
  • 面积图: 适合用“积木堆叠“或“装水“的类比解释
  • 透明度: opacity范围是0到1,0表示完全透明,1表示不透明
  • 交互功能: 鼠标悬停在图表上时,会显示详细的数据提示框

15.4.5 折线图的标记功能

pyecharts可以在折线图上自动标记特殊点:

from pyecharts.charts import Line
from pyecharts import options as opts

line = Line()

line.add_xaxis(["1月", "2月", "3月", "4月", "5月", "6月"])
line.add_yaxis(
    "销售额(万元)",
    [10, 15, 13, 18, 20, 25],
    # 标记点:最大值、最小值
    markpoint_opts=opts.MarkPointOpts(
        data=[
            opts.MarkPointItem(type_="max", name="最大值"),
            opts.MarkPointItem(type_="min", name="最小值")
        ]
    ),
    # 标记线:平均值
    markline_opts=opts.MarkLineOpts(
        data=[
            opts.MarkLineItem(type_="average", name="平均值")
        ]
    )
)

line.set_global_opts(title_opts={"text": "上半年销售额"})

line.render("marked_line.html")

标记说明:

  • 标记点: 用圆点标记特殊位置(最大值、最小值、自定义点)
  • 标记线: 用直线标记特殊位置(平均值、自定义线)

实践练习5:游戏等级提升记录

任务: 假设你在一周内玩游戏,每天记录等级提升:

  • x轴: 周一到周日
  • y轴: 等级(假设起始等级1,每天提升不同)
  • 要求:标记最大提升的那一天,显示平均等级

示例数据:

levels = [1, 3, 5, 8, 10, 15, 18]
gains = [0, 2, 2, 3, 2, 5, 3]  # 每天提升的等级

答案:

点击查看完整代码
from pyecharts.charts import Line
from pyecharts import options as opts

levels = [1, 3, 5, 8, 10, 15, 18]
days = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]

line = Line()

line.add_xaxis(days)
line.add_yaxis(
    "等级",
    levels,
    markpoint_opts=opts.MarkPointOpts(
        data=[
            opts.MarkPointItem(type_="max", name="最高等级"),
            opts.MarkPointItem(type_="min", name="最低等级")
]
    ),
    markline_opts=opts.MarkLineOpts(
        data=[
            opts.MarkLineItem(type_="average", name="平均等级")
        ]
    )
)

line.set_global_opts(
    title_opts={"text": "游戏等级提升记录"},
    yaxis_opts=opts.AxisOpts(name="等级"),
    xaxis_opts=opts.AxisOpts(name="日期")
)

line.render("game_levels.html")

15.5 饼图 - 显示比例关系

15.5.1 饼图的应用场景

**饼图(Pie Chart)**用圆形的扇形大小表示各部分占整体的比例。

饼图适合用来:

  • 显示占比: 班级男女生比例
  • 分配关系: 零花钱的用途分配
  • 组成结构: 家庭支出中各项费用的占比

生活中的饼图例子:

  • 预算分配图
  • 选举结果占比
  • 手机存储空间使用情况

重要: 饼图的数据加起来应该是100%(或1),表示一个整体。

15.5.2 创建一个简单的饼图

让我们制作一个显示班级运动爱好的饼图:

from pyecharts.charts import Pie

# 数据
sports = ["篮球", "足球", "羽毛球", "乒乓球", "跑步", "游泳"]
counts = [15, 12, 8, 10, 6, 9]

# 创建饼图
pie = Pie()

# 添加数据
pie.add("", list(zip(sports, counts)))

# 设置标题
pie.set_global_opts(title_opts={"text": "班级运动喜好分布"})

# 生成图表
pie.render("sports_pie.html")

代码解析:

  • list(zip(sports, counts))将两个列表合并成:[("篮球", 15), ("足球", 12), ...]
  • 饼图的add()方法的第一个参数是系列名称(可以为空字符串)

15.5.3 饼图的样式设置

1. 显示百分比和标签

默认情况下,饼图会显示类别名称。我们可以同时显示百分比:

from pyecharts.charts import Pie
from pyecharts import options as opts

sports = ["篮球", "足球", "羽毛球", "乒乓球", "跑步", "游泳"]
counts = [15, 12, 8, 10, 6, 9]

pie = Pie()

pie.add(
    "",
    list(zip(sports, counts)),
    label_opts=opts.LabelOpts(formatter="{b}: {d}%")  # {b}=名称, {d}=百分比
)

pie.set_global_opts(title_opts={"text": "班级运动喜好分布"})

pie.render("sports_pie_percent.html")

formatter参数说明:

  • {a}: 系列名称
  • {b}: 数据项名称(如“篮球“)
  • {c}: 数值(如15)
  • {d}: 百分比(如18.07%)

2. 环形饼图

环形饼图比普通饼图更美观,中间是空心的:

from pyecharts.charts import Pie
from pyecharts import options as opts

sports = ["篮球", "足球", "羽毛球", "乒乓球", "跑步", "游泳"]
counts = [15, 12, 8, 10, 6, 9]

pie = Pie()

pie.add(
    "",
    list(zip(sports, counts)),
    radius=["30%", "70%"]  # 内圆半径30%,外圆半径70%
)

pie.set_global_opts(title_opts={"text": "班级运动喜好分布"})

pie.render("donut_pie.html")

radius参数:

  • 第一个值: 内圆半径
  • 第二个值: 外圆半径
  • 两个值越接近,环形越细

给家长的小贴士

  • 百分比概念: 饼图是帮助孩子理解百分比的绝佳工具
  • 数据归一化: 如果数据加起来不是100%,pyecharts会自动计算百分比
  • 颜色识别: 教孩子如何从饼图的颜色和面积大小直观地理解比例
  • 环形 vs 实心: 环形饼图可以在中间显示总数或标题,更美观

实践练习6:我的零花钱花哪里了

任务: 制作一个饼图,显示你一个月零花钱的使用情况:

  • 类别: 零食、文具、玩具、游戏、其他
  • 数据: 自己设定一个合理的分配(如: 30%, 20%, 15%, 25%, 10%)
  • 要求:使用环形饼图,显示百分比

示例数据:

items = ["零食", "文具", "玩具", "游戏", "其他"]
money = [60, 40, 30, 50, 20]  # 单位:元

答案:

点击查看完整代码
from pyecharts.charts import Pie
from pyecharts import options as opts

items = ["零食", "文具", "玩具", "游戏", "其他"]
money = [60, 40, 30, 50, 20]

pie = Pie()

pie.add(
    "",
    list(zip(items, money)),
    radius=["35%", "65%"],  # 环形
    label_opts=opts.LabelOpts(formatter="{b}: {c}元 ({d}%)")
)

pie.set_global_opts(
    title_opts={"text": "我的零花钱使用情况"},
    legend_opts=opts.LegendOpts(orient="vertical", pos_left="left")
)

pie.render("allowance_spending.html")

运行后,你会看到一个漂亮的环形饼图,每个部分显示金额和百分比!

15.5.4 饼图的Rose图

Rose图(玫瑰图)是饼图的变体,扇形的半径不同:

from pyecharts.charts import Pie
from pyecharts import options as opts

sports = ["篮球", "足球", "羽毛球", "乒乓球", "跑步", "游泳"]
counts = [15, 12, 8, 10, 6, 9]

pie = Pie()

pie.add(
    "",
    list(zip(sports, counts)),
    radius="60%",  # 半径
    rosetype="radius"  # 设置为玫瑰图
)

pie.set_global_opts(title_opts={"text": "班级运动喜好分布(Rose图)"})

pie.render("rose_pie.html")

Rose图特点:

  • 每个扇形的半径不同,数据越大,扇形越大
  • 适合数据差异较大的情况
  • 视觉效果更丰富

给家长的小贴士

  • Rose图 vs 饼图: Rose图强调数值差异,饼图强调比例关系
  • 选择建议: 数据差异大时用Rose图,数据相近时用普通饼图
  • 交互功能: 点击某个扇形,该扇形会突出显示

15.5.5 饼图的嵌套

饼图可以嵌套,显示多层数据:

from pyecharts.charts import Pie
from pyecharts import options as opts

# 内圈数据
inner_data = [("电子产品", 60), ("服装", 30), ("食品", 50)]
# 外圈数据(细分)
outer_data = [
    ("手机", 30), ("电脑", 20), ("耳机", 10),
    ("男装", 15), ("女装", 15),
    ("零食", 25), ("饮料", 15), ("水果", 10)
]

pie = Pie()

# 添加内圈
pie.add(
    "",
    inner_data,
    radius=["0%", "35%"],  # 从中心到35%的区域
    label_opts=opts.LabelOpts(formatter="{b}: {d}%")
)

# 添加外圈
pie.add(
    "",
    outer_data,
    radius=["45%", "70%"],  # 从45%到70%的区域
    label_opts=opts.LabelOpts(formatter="{b}: {d}%")
)

pie.set_global_opts(title_opts={"text": "销售额分布(嵌套饼图)"})

pie.render("nested_pie.html")

使用场景: 嵌套饼图适合显示“大类-小类“的层级关系,如“运动-球类、跑步、游泳“。

实践练习7:我的时间分配

任务: 制作一个嵌套饼图,显示你一天的时间分配:

内圈(大类):

  • 睡眠: 9小时
  • 上学: 8小时
  • 作业: 3小时
  • 娱乐: 4小时

外圈(娱乐细分):

  • 看电视: 1.5小时
  • 玩游戏: 1小时
  • 运动: 1小时
  • 其他: 0.5小时

答案:

点击查看完整代码
from pyecharts.charts import Pie
from pyecharts import options as opts

# 内圈: 大类
inner_data = [
    ("睡眠", 9), ("上学", 8), ("作业", 3), ("娱乐", 4)
]

# 外圈: 娱乐细分
outer_data = [
    ("看电视", 1.5), ("玩游戏", 1), ("运动", 1), ("其他", 0.5),
    # 补充其他类别的数据(为了图形完整)
    ("睡眠细分", 9), ("上学细分", 8), ("作业细分", 3)
]

pie = Pie()

pie.add(
    "",
    inner_data,
    radius=["0%", "35%"],
    label_opts=opts.LabelOpts(formatter="{b}: {c}小时")
)

pie.add(
    "",
    outer_data,
    radius=["45%", "70%"],
    label_opts=opts.LabelOpts(formatter="{b}: {c}小时")
)

pie.set_global_opts(title_opts={"text": "我的一天时间分配"})

pie.render("my_day_pie.html")

提示: 这个例子展示了嵌套饼图的应用,但外圈数据不完全对称,实际情况中可能需要更完整的数据分类。


15.6 综合项目:制作班级成绩分析报告

现在我们已经学会了柱状图、折线图、饼图,让我们做一个综合项目:班级成绩分析报告系统。

这个项目会综合运用我们学到的所有知识!

15.6.1 项目需求

假设你是班长,老师给你一份班级成绩数据,你需要:

  1. 柱状图显示每个学生的总分排名
  2. 折线图显示班级平均分的变化趋势
  3. 饼图显示各分数段的人数分布
  4. 生成一个完整的HTML报告

原始数据:

姓名语文数学英语物理化学
小明8592788890
小红9088959294
小刚7885828083
小丽9295889091
小华8890918589
小强8082857880
小芳9590929593

15.6.2 项目实现

让我们一步步完成这个项目:

步骤1: 准备数据

# 学生姓名
students = ["小明", "小红", "小刚", "小丽", "小华", "小强", "小芳"]

# 各科成绩
chinese = [85, 90, 78, 92, 88, 80, 95]
math = [92, 88, 85, 95, 90, 82, 90]
english = [78, 95, 82, 88, 91, 85, 92]
physics = [88, 92, 80, 90, 85, 78, 95]
chemistry = [90, 94, 83, 91, 89, 80, 93]

# 计算总分
total_scores = []
for i in range(len(students)):
    total = chinese[i] + math[i] + english[i] + physics[i] + chemistry[i]
    total_scores.append(total)

print("总分:", total_scores)

运行结果:

总分: [433, 459, 408, 456, 443, 405, 465]

步骤2: 柱状图 - 总分排名

from pyecharts.charts import Bar
from pyecharts import options as opts

# 创建柱状图
bar = Bar()

# 添加数据
bar.add_xaxis(students)
bar.add_yaxis(
    "总分",
    total_scores,
    label_opts=opts.LabelOpts(is_show=True)  # 显示数值
)

# 设置全局选项
bar.set_global_opts(
    title_opts={"text": "班级总分排名"},
    yaxis_opts=opts.AxisOpts(name="分数"),
    xaxis_opts=opts.AxisOpts(name="学生"),
    toolbox_opts=opts.ToolboxOpts(is_show=True)  # 显示工具栏
)

# 生成图表
bar.render("class_ranking.html")

步骤3: 折线图 - 科目平均分

from pyecharts.charts import Line

# 计算各科平均分
chinese_avg = sum(chinese) // len(chinese)
math_avg = sum(math) // len(math)
english_avg = sum(english) // len(english)
physics_avg = sum(physics) // len(physics)
chemistry_avg = sum(chemistry) // len(chemistry)

subjects = ["语文", "数学", "英语", "物理", "化学"]
avg_scores = [chinese_avg, math_avg, english_avg, physics_avg, chemistry_avg]

# 创建折线图
line = Line()

line.add_xaxis(subjects)
line.add_yaxis(
    "平均分",
    avg_scores,
    markpoint_opts=opts.MarkPointOpts(
        data=[
            opts.MarkPointItem(type_="max", name="最高分"),
            opts.MarkPointItem(type_="min", name="最低分")
        ]
    )
)

line.set_global_opts(
    title_opts={"text": "各科目平均分"},
    yaxis_opts=opts.AxisOpts(name="分数"),
    xaxis_opts=opts.AxisOpts(name="科目")
)

line.render("subject_averages.html")

步骤4: 饼图 - 分数段分布

from pyecharts.charts import Pie

# 统计分数段
ranges = ["优秀(90分以上)", "良好(80-89)", "及格(60-79)", "不及格(60分以下)"]
counts = [0, 0, 0, 0]

# 统计总分段
for score in total_scores:
    avg = score / 5  # 计算平均分
    if avg >= 90:
        counts[0] += 1
    elif avg >= 80:
        counts[1] += 1
    elif avg >= 60:
        counts[2] += 1
    else:
        counts[3] += 1

print("分数段分布:", counts)

# 创建饼图
pie = Pie()

pie.add(
    "",
    list(zip(ranges, counts)),
    radius=["30%", "60%"],
    label_opts=opts.LabelOpts(formatter="{b}: {c}人 ({d}%)")
)

pie.set_global_opts(title_opts={"text": "班级成绩分布"})

pie.render("score_distribution.html")

运行结果:

分数段分布: [3, 3, 1, 0]  # 优秀3人,良好3人,及格1人,不及格0人

步骤5: 组合所有图表

pyecharts提供了一个Page类,可以把多个图表组合在一个HTML页面:

from pyecharts.charts import Bar, Line, Pie
from pyecharts import options as opts
from pyecharts.commons.utils import JsCode

# 数据准备(省略,参考上面的代码)

# 创建三个图表(代码省略)
bar = Bar()
# ... 添加数据和选项

line = Line()
# ... 添加数据和选项

pie = Pie()
# ... 添加数据和选项

# 组合图表
from pyecharts.charts import Page

page = Page(layout=Page.SimplePageLayout)

page.add(
    bar,
    line,
    pie
)

page.render("class_report.html")

运行结果: 打开class_report.html,你会看到三个图表从上到下排列在一个页面中,形成一个完整的成绩分析报告!

给家长的小贴士

  • 项目式学习: 这个综合项目能培养孩子的整体思维和问题解决能力
  • 数据收集: 鼓励孩子用真实数据(如自己班级的成绩)替换示例数据
  • 分析能力: 让孩子根据图表分析班级情况(如:哪科最难,谁进步最大)
  • 逐步完成: 强调分步骤完成,不要试图一次写完所有代码
  • 调试技巧: 如果某个图表有问题,单独运行它的代码,找出错误

综合练习8:完整的数据分析报告

任务: 选择一个你感兴趣的主题,制作一个完整的数据分析报告:

主题建议:

  1. 运动数据分析: 班级同学最喜欢的运动类型
  2. 游戏数据分析: 你和朋友的游戏等级、胜率、场次对比
  3. 零花钱分析: 一个月的零花钱来源和用途
  4. 阅读分析: 这学期读了多少本书,各类书的比例
  5. 自定义主题: 其他你感兴趣的数据

要求:

  1. 至少包含3种图表(柱状图、折线图、饼图)
  2. 每个图表都要有标题和坐标轴说明
  3. 用Page组合所有图表
  4. 根据图表写出3条分析结论

示例代码框架:

点击查看代码框架
from pyecharts.charts import Bar, Line, Pie
from pyecharts.charts import Page
from pyecharts import options as opts

# 步骤1: 准备数据
# TODO: 填写你的数据

# 步骤2: 创建柱状图
bar = Bar()
# TODO: 添加数据和选项

# 步骤3: 创建折线图
line = Line()
# TODO: 添加数据和选项

# 步骤4: 创建饼图
pie = Pie()
# TODO: 添加数据和选项

# 步骤5: 组合所有图表
page = Page()
page.add(bar, line, pie)
page.render("my_report.html")

print("报告已生成!")

15.7 进阶技巧:让图表更美观

15.7.1 使用主题

pyecharts提供了多种预设主题,可以快速改变图表的配色:

from pyecharts.charts import Bar
from pyecharts import options as opts
from pyecharts.globals import ThemeType

# 使用DARK主题
bar = Bar(init_opts=opts.InitOpts(theme=ThemeType.DARK))

bar.add_xaxis(["苹果", "香蕉", "橙子", "草莓", "葡萄"])
bar.add_yaxis("价格", [8, 3, 6, 15, 12])

bar.set_global_opts(title_opts={"text": "水果价格表"})

bar.render("themed_chart.html")

可用主题:

  • ThemeType.LIGHT: 浅色主题(默认)
  • ThemeType.DARK: 深色主题
  • ThemeType.ROMA: 罗马主题
  • ThemeType.SHINE: 闪亮主题
  • ThemeType.VINTAGE: 复古主题

15.7.2 自定义颜色

如果你想自定义图表的颜色,可以使用颜色列表:

from pyecharts.charts import Pie
from pyecharts import options as opts

items = ["篮球", "足球", "羽毛球", "乒乓球", "跑步", "游泳"]
counts = [15, 12, 8, 10, 6, 9]

pie = Pie()

pie.add(
    "",
    list(zip(items, counts)),
    # 自定义颜色
    color=["#FF6B6B", "#4ECDC4", "#45B7D1", "#FFA07A", "#98D8C8", "#F7DC6F"]
)

pie.set_global_opts(title_opts={"text": "班级运动喜好"})

pie.render("custom_colors.html")

颜色代码说明:

  • 使用十六进制颜色码(如#FF6B6B)
  • 可以在线搜索“hex color picker“选择颜色
  • 颜色列表长度应该与数据项数量一致

15.7.3 添加提示框和工具栏

from pyecharts.charts import Bar
from pyecharts import options as opts

bar = Bar()

bar.add_xaxis(["第一季度", "第二季度", "第三季度", "第四季度"])
bar.add_yaxis("销售额", [120, 200, 150, 180])

bar.set_global_opts(
    # 提示框配置
    tooltip_opts=opts.TooltipOpts(
        trigger="axis",  # 坐标轴触发
        axis_pointer_type="shadow"  # 阴影指示器
    ),
    # 工具栏配置
    toolbox_opts=opts.ToolboxOpts(
        is_show=True,
        feature={
            "saveAsImage": {},  # 保存为图片
            "dataView": {},     # 数据视图
            "restore": {}        # 还原
        }
    ),
    # 区域缩放
    datazoom_opts=opts.DataZoomOpts(is_show=True)
)

bar.render("advanced_features.html")

功能说明:

  • 提示框: 鼠标悬停时显示详细信息
  • 工具栏: 右上角显示保存、数据视图等工具
  • 区域缩放: 可以缩放查看部分数据

给家长的小贴士

  • 颜色搭配: 教孩子基本的配色原则(对比色、同类色)
  • 主题选择: 不同主题适合不同场合(如DARK适合夜间查看)
  • 功能叠加: 不要一次添加太多功能,会影响图表的可读性
  • 审美培养: 鼓励孩子尝试不同的配色,培养审美能力

15.8 常见错误和调试

15.8.1 数据类型错误

错误代码:

from pyecharts.charts import Bar

bar = Bar()
bar.add_xaxis("苹果")  # 错误:应该是列表
bar.add_yaxis("价格", 8)  # 错误:应该是列表

错误信息:

TypeError: Input x_axis data must be list

正确代码:

bar.add_xaxis(["苹果"])  # 使用列表
bar.add_yaxis("价格", [8])  # 使用列表

给家长的小贴士

  • 列表 vs 单个值: pyecharts需要列表,即使只有一个数据也要用[8]
  • 仔细阅读错误信息: 错误信息会明确告诉你是哪个参数有问题

15.8.2 数据长度不一致

错误代码:

bar.add_xaxis(["苹果", "香蕉", "橙子"])
bar.add_yaxis("价格", [8, 3])  # 只有2个数据,少了1个

错误信息:

AssertionError: x_axis data and y_axis data must be same length

解决方法: 确保x轴和y轴的数据数量一致:

bar.add_xaxis(["苹果", "香蕉", "橙子"])
bar.add_yaxis("价格", [8, 3, 6])  # 3个数据

15.8.3 文件路径错误

错误代码:

bar.render("C:\Users\用户名\桌面\chart.html")

错误信息:

SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes

原因: Python中\是转义字符,\U会被认为是Unicode转义。

解决方法1: 使用原始字符串

bar.render(r"C:\Users\用户名\桌面\chart.html")  # 注意r前缀

解决方法2: 使用正斜杠

bar.render("C:/Users/用户名/桌面/chart.html")  # 使用/

解决方法3: 只写文件名(推荐)

bar.render("chart.html")  # 保存到当前目录

给家长的小贴士

  • 路径问题: 小学生可能不理解文件路径,建议让他们只写文件名
  • 文件查找: 生成的HTML文件在当前工作目录,可以让孩子用文件管理器搜索
  • 桌面快捷方式: 在桌面上创建一个“我的图表“文件夹,方便查找

15.8.4 HTML文件打不开或空白

问题1: HTML文件打开后是空白页

原因: 代码运行时出现错误,但没有生成完整的HTML文件。

解决方法: 在Python中运行代码时,查看是否有错误提示。常见的错误:

  • 拼写错误:add_xasis应该是add_xaxis
  • 缺少括号或引号
  • 变量名未定义

问题2: 浏览器打不开HTML文件

原因: 浏览器太老,不支持某些特性。

解决方法: 更新浏览器或使用Chrome、Edge、Firefox等现代浏览器。

15.8.5 调试技巧

技巧1: 逐步验证

不要一次写完所有代码,而是分步测试:

# 步骤1: 测试数据
print("x轴:", ["苹果", "香蕉"])
print("y轴:", [8, 3])

# 步骤2: 测试图表创建
bar = Bar()
print("图表创建成功")

# 步骤3: 测试添加数据
bar.add_xaxis(["苹果", "香蕉"])
bar.add_yaxis("价格", [8, 3])
print("数据添加成功")

# 步骤4: 测试渲染
bar.render("test.html")
print("渲染完成")

技巧2: 简化数据

如果数据很复杂,先用简单数据测试:

# 复杂数据可能有1000条
# x_data = [...]  # 1000条
# y_data = [...]  # 1000条

# 先用3条数据测试
x_data = ["A", "B", "C"]
y_data = [1, 2, 3]

bar = Bar()
bar.add_xaxis(x_data)
bar.add_yaxis("测试", y_data)
bar.render("simple_test.html")

技巧3: 查看HTML源代码

如果图表显示不正常,用文本编辑器打开HTML文件,搜索"code":

<script>
  var chart = echarts.init(...);
  var option = {
    "title": {...},
    "tooltip": {...},
    ...
  };
  chart.setOption(option);
</script>

查看是否有"error""undefined"等异常信息。

给家长的小贴士

  • 调试思维: 教孩子“分而治之“的调试方法,把大问题拆成小问题
  • 错误是朋友: 强调错误信息是帮助我们解决问题的线索
  • 保存中间版本: 鼓励孩子定期保存代码的备份,出错时可以回退

15.9 本章小结

15.9.1 核心知识点回顾

1. 数据可视化的重要性

  • 把抽象的数字变成直观的图形
  • 让数据更容易理解和分析
  • 帮助发现数据背后的规律

2. pyecharts库的使用流程

安装: pip install pyecharts
  ↓
导入: from pyecharts.charts import Bar
  ↓
创建: bar = Bar()
  ↓
数据: bar.add_xaxis() / bar.add_yaxis()
  ↓
选项: bar.set_global_opts()
  ↓
渲染: bar.render("file.html")

3. 三种常见图表

图表类型用途适合场景
柱状图比较不同类别排名、对比、统计
折线图显示变化趋势成绩进步、气温变化、股票走势
饼图显示比例关系费用分配、偏好分布、成分构成

4. 图表的配置选项

  • 标题: title_opts
  • 坐标轴: xaxis_opts / yaxis_opts
  • 标签: label_opts
  • 图例: legend_opts
  • 工具栏: toolbox_opts
  • 提示框: tooltip_opts

5. 高级功能

  • 多系列: 在一个图表上显示多组数据
  • 堆叠: 堆叠柱状图显示部分与整体
  • 嵌套: 嵌套饼图显示层级关系
  • 组合: 用Page组合多个图表

15.9.2 能力检查表

请孩子自我检查以下技能是否掌握:

  • 能独立安装pyecharts库
  • 能创建并保存一个简单的柱状图
  • 能创建并保存一个简单的折线图
  • 能创建并保存一个简单的饼图
  • 能为图表添加标题和坐标轴说明
  • 能在一个图表上显示多组数据
  • 能使用Page组合多个图表
  • 能读懂简单的错误信息并调试
  • 能独立完成一个数据分析小项目

15.9.3 常见问题快速参考

问题解决方法
安装失败使用国内镜像源:pip install pyecharts -i https://pypi.tuna.tsinghua.edu.cn/simple
中文乱码在文件第一行添加# -*- coding: utf-8 -*-
数据长度不一致检查x轴和y轴的数据数量是否相同
HTML文件找不到只写文件名(如chart.html),保存到当前目录
图表空白检查浏览器,使用Chrome或Edge等现代浏览器

15.10 挑战练习

挑战1: 天气数据分析

任务: 从网上查找你所在城市最近一周的天气数据(最高气温、最低气温、天气状况),制作:

  1. 折线图:显示气温变化
  2. 饼图:显示不同天气状况的比例(如:晴天3天,阴天2天,雨天2天)

要求:

  • 使用真实数据
  • 图表有标题和说明
  • 添加数据标记点(最高温、最低温)

挑战2: 我的学习时间统计

任务: 记录自己一周每天的学习时间,制作:

  1. 柱状图:显示每天的学习时长
  2. 饼图:显示学习时间的分配(如:周一2小时,周二3小时…)

要求:

  • 数据真实可靠
  • 分析哪天学习时间最长/最短
  • 找出学习规律并给出建议

挑战3: 游戏数据分析

任务: 如果你有喜欢的游戏,记录游戏数据,制作:

  1. 柱状图:你和朋友的等级对比
  2. 折线图:最近10次的胜率变化
  3. 饼图:使用英雄/武器的频率

要求:

  • 至少包含3种图表
  • 用Page组合所有图表
  • 写出分析结论(如:我最擅长哪个英雄)

挑战4: 班级调查报告

任务: 设计一个班级调查(如:最喜欢的科目、最喜欢的运动、每天的学习时间…),收集数据后制作完整的分析报告。

要求:

  • 调查至少10名同学
  • 使用柱状图、折线图、饼图
  • 报告包含:
    • 调查主题
    • 数据来源
    • 图表分析
    • 你的建议

挑战5: 创意数据可视化

任务: 选择一个你感兴趣的主题(如:我的阅读记录、零花钱追踪、运动数据分析…),制作一个创意数据可视化项目。

要求:

  • 数据真实且有连续性(至少记录一周)
  • 至少3个图表
  • 尝试使用主题、自定义颜色等高级功能
  • 向同学展示你的报告,并收集反馈

15.11 下一步预告

恭喜你完成了第15章的学习!你已经掌握了:

  • ✅ 数据可视化的基本概念
  • ✅ 柱状图、折线图、饼图的制作
  • ✅ pyecharts库的使用方法
  • ✅ 完整的数据分析报告制作

第16章:图形程序中,你将学习:

  • 使用tkinter库创建图形界面
  • 按钮、文本框、标签等控件
  • 制作带界面的交互程序

想象一下,不再是在黑色的命令行窗口输入命令,而是有漂亮的窗口、按钮、输入框…这就是图形界面的魅力!我们下一章见!


附录:pyecharts速查表

常用图表类

from pyecharts.charts import Bar, Line, Pie

# 柱状图
bar = Bar()
bar.add_xaxis(x_data)
bar.add_yaxis(series_name, y_data)
bar.render("bar.html")

# 折线图
line = Line()
line.add_xaxis(x_data)
line.add_yaxis(series_name, y_data)
line.render("line.html")

# 饼图
pie = Pie()
pie.add(series_name, data)
pie.render("pie.html")

常用配置选项

from pyecharts import options as opts

# 标题
title_opts={"text": "图表标题"}

# 坐标轴
xaxis_opts=opts.AxisOpts(name="x轴名称")
yaxis_opts=opts.AxisOpts(name="y轴名称")

# 标签
label_opts=opts.LabelOpts(is_show=True)  # 显示数据标签

# 提示框
tooltip_opts=opts.TooltipOpts(trigger="axis")

# 工具栏
toolbox_opts=opts.ToolboxOpts(is_show=True)

# 数据缩放
datazoom_opts=opts.DataZoomOpts(is_show=True)

常用主题

from pyecharts.globals import ThemeType

bar = Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))
bar = Bar(init_opts=opts.InitOpts(theme=ThemeType.DARK))
bar = Bar(init_opts=opts.InitOpts(theme=ThemeType.ROMA))
bar = Bar(init_opts=opts.InitOpts(theme=ThemeType.SHINE))

Page组合图表

from pyecharts.charts import Page

page = Page()
page.add(chart1, chart2, chart3)
page.render("combined.html")

祝贺你完成第15章的学习! 🎉

现在,你已经能用Python制作专业的数据报表了!试试用你学到的技能,记录和分析生活中的数据吧!

第16章 图形程序 - 让程序有“面子“

引言:从黑白世界到彩色世界

想象一下,如果你一直在用这样的方式和使用电脑程序:

请输入你的名字:小明
请输入你的年龄:12
你好,小明!你今年12岁了。

这是我们在前面章节中一直使用的命令行程序(Command Line Interface,简称CLI)。它的特点是:

  • 只能输入文字
  • 只能显示文字
  • 需要记住各种命令
  • 界面简陋(黑底白字)

但是现在,你用的QQ、微信、王者荣耀、我的世界……这些程序是什么样子的?

  • 有漂亮的窗口
  • 可以点击按钮
  • 可以在输入框里打字
  • 可以看到图片颜色
  • 可以用鼠标操作

这种程序叫图形界面程序(Graphical User Interface,简称GUI)!

什么是GUI?

GUI(Graphical User Interface)就是图形用户界面。让我们通过对比来理解:

特性命令行程序(CLI)图形界面程序(GUI)
外观黑底白字,只有文字彩色窗口,有图片、按钮、图标
操作方式只能用键盘输入命令可以用鼠标点击、拖拽
使用难度需要记住命令直观易懂,看图标就知道功能
例子我们之前的所有程序QQ、微信、游戏、浏览器

为什么学习GUI程序?

学习制作图形界面程序,你将能够:

  1. 制作真正“像样“的软件

    • 不再只是黑白的文字界面
    • 可以有漂亮的按钮、输入框、图片
    • 让你的程序看起来更专业
  2. 改善用户体验

    • 用户不需要记住命令
    • 看到按钮就知道能做什么
    • 操作更直观、更方便
  3. 将之前的程序“升级“

    • 把第14章的命令行课表查询程序改成带按钮的
    • 把计算器程序改成有按钮和显示屏幕的
    • 把猜数字游戏改成有界面的
  4. 理解日常软件的原理

    • QQ的聊天窗口是怎么做出来的?
    • 游戏的设置界面是怎么设计的?
    • 学完这一章,你就知道答案了!

本章学习路线

本章将教你使用tkinter库制作图形界面程序,同时融入几何和坐标知识:

第1步:认识tkinter库 - Python自带的GUI工具
   ↓
第2步:复习数学坐标知识 - 窗口坐标系统
   ↓
第3步:创建第一个窗口 - 设置大小和位置
   ↓
第4步:添加组件 - 按钮、输入框、标签(运用坐标知识)
   ↓
第5步:布局管理 - 让组件排列整齐(运用几何知识)
   ↓
第6步:事件处理 - 让按钮"动"起来
   ↓
第7步:综合项目 - 改写CLI程序为GUI版本

👨‍🏫 给家长的小贴士

  • GUI是现代软件的标准: 几乎所有现代软件都使用图形界面
  • 培养用户思维: 让孩子思考“怎么让用户更方便地使用程序“
  • 激发创造力: 图形界面可以自由设计颜色、布局,孩子会很有成就感
  • 实际应用: 可以制作家庭记账本、学习计划表等实用工具
  • 本章重点融入的数学知识:
    • 坐标系: 计算机窗口的坐标原点在左上角(与数学坐标系不同)
    • 位置和尺寸: 组件的(x, y)位置,宽度width,高度height
    • 面积计算: 窗口面积 = 宽 × 高
    • 几何图形: 矩形、正方形在界面布局中的应用
  • 本章融入的计算机知识:
    • 显卡和显示器: 图形如何显示在屏幕上
    • 事件驱动: 点击按钮如何触发程序执行
    • CPU的中断机制: 计算机如何响应鼠标和键盘操作

tkinter是什么?

tkinter是Python自带的一个图形界面库。它的名字来源于:

  • tk: 一个著名的图形界面工具包(Tool Kit)
  • inter: interface(界面)的缩写
  • t: 表示这是Tk的Python接口( Tcl/Tk 的Python接口)

为什么要用tkinter?

  1. 免费且内置:Python安装时就自带了,不需要额外安装
  2. 简单易学:代码简洁,容易上手
  3. 功能够用:可以制作常见的界面元素
  4. 跨平台:在Windows、Mac、Linux上都能运行

tkinter能做什么?

  • ✅ 创建窗口
  • ✅ 添加按钮
  • ✅ 添加输入框
  • ✅ 添加文字标签
  • ✅ 添加图片
  • ✅ 添加下拉菜单
  • ✅ 添加复选框和单选按钮
  • ✅ 添加滑动条
  • ✅ ……还有很多其他组件

给家长的小贴士

  • GUI是现代软件的标准:几乎所有现代软件都使用图形界面
  • 培养用户思维:让孩子思考“怎么让用户更方便地使用程序“
  • 激发创造力:图形界面可以自由设计颜色、布局,孩子会很有成就感
  • 实际应用:可以制作家庭记账本、学习计划表等实用工具
  • 学习曲线:GUI比命令行程序复杂一些,需要更多耐心
  • 建议:先让孩子熟悉一个简单例子,再鼓励他设计自己的界面

16.1 第一个窗口程序

16.1.1 最简单的窗口

让我们从最简单的开始——创建一个空白窗口!

# 导入tkinter库
import tkinter as tk

# 创建主窗口
root = tk.Tk()

# 进入消息循环(让窗口一直显示)
root.mainloop()

运行结果: 弹出一个空白窗口!

代码解释:

  1. import tkinter as tk

    • 导入tkinter库
    • as tk表示给tkinter起个简短的别名,以后用tk代替tkinter
  2. root = tk.Tk()

    • 创建一个主窗口
    • Tk()是创建主窗口的函数
    • root是这个窗口的名字(你可以叫它windowmain_window
  3. root.mainloop()

    • 进入消息循环
    • 这是最重要的一行!
    • 让窗口一直显示,等待用户操作
    • 如果没有这一行,窗口会一闪而过

小知识:什么是“消息循环“?

想象一下你开了一家店:

  • root = tk.Tk():把店面装修好,准备开门
  • root.mainloop():你站在店门口,一直等待顾客进来
    • 如果顾客来了(点击按钮、输入文字等),你就处理顾客的需求
    • 如果没有顾客,你就继续等待
    • 如果没有这一步,店开一下就关门了!

16.1.2 给窗口起个名字

现在我们的窗口标题是默认的“tk“,让我们改个名字:

import tkinter as tk

root = tk.Tk()

# 设置窗口标题
root.title("我的第一个GUI程序")

root.mainloop()

运行结果: 窗口标题变成了“我的第一个GUI程序“!

16.1.3 设置窗口大小

窗口默认大小可能不太合适,让我们设置一下:

import tkinter as tk

root = tk.Tk()
root.title("我的第一个GUI程序")

# 设置窗口大小(宽x高)
root.geometry("400x300")

root.mainloop()

运行结果: 窗口变成了400像素宽、300像素高!

注意:

  • geometry的意思是“几何形状“
  • "400x300"中的x是字母x,不是乘号!
  • 单位是像素(Pixel,电脑屏幕最小的显示单位)

16.1.4 综合示例:漂亮的窗口

import tkinter as tk

root = tk.Tk()
root.title("我的第一个GUI程序")
root.geometry("500x400")

# TODO: 后面我们会在这里添加按钮、输入框等组件

root.mainloop()

⚠️ 常见错误

错误1:忘记mainloop()

import tkinter as tk
root = tk.Tk()
# 忘记写 root.mainloop()

结果:窗口一闪而过,或者根本不显示

错误2:大小写成乘号

root.geometry(400x300)  # 错误!x会被当作变量名

正确写法root.geometry("400x300")(必须加引号,且x是字母)

🎮 实践练习1:创建你的第一个窗口

任务:创建一个窗口,满足以下要求:

  • 标题为:“Python图形界面”
  • 大小为:600×400
  • 尝试修改大小,看看不同尺寸的效果

提示:使用title()geometry()方法

🔍 查看答案
import tkinter as tk

root = tk.Tk()
root.title("Python图形界面")
root.geometry("600x400")

root.mainloop()

16.2 认识计算机坐标系 📐

在开始添加组件之前,让我们先学习计算机的坐标系,这对放置组件非常重要!

16.2.1 计算机坐标系 vs 数学坐标系

你在数学课上学过平面直角坐标系:

  • 原点: 在左下角
  • x轴: 向右为正方向
  • y轴: 向上为正方向
  • 坐标: (x, y),x是横坐标,y是纵坐标

数学坐标系示例:

      y↑
       |
   4   |    • (3, 4)
   3   |
   2   |
   1   | • (1, 1)
   0---+---+---+---+---→x
      0   1   2   3   4

但是,计算机的坐标系完全不同!

16.2.2 计算机窗口坐标系

计算机坐标系的特点:

  • 原点: 在左上角(记住这个重要差异!)
  • x轴: 向右为正方向(和数学一样)
  • y轴: 向下为正方向(和数学相反!)
  • 单位: 像素(Pixel)

计算机坐标系示例:

   0   1   2   3   4 →x
 0---+---+---+---+---
 1   |   • (1, 1)
 2   |
 3   |       • (3, 3)
 4   |
 ↓y

关键差异:

  • 数学坐标系:y向上,计算机坐标系:y向下
  • 数学坐标系:原点在左下,计算机坐标系:原点在左上

为什么计算机坐标系是这样的?

  • 显示器扫描是从左上角开始的(像我们看书,从左上到右下)
  • 这样设计符合西方文字的阅读习惯(从上到下,从左到右)

👨‍🏫 给家长的小贴士

  • 类比方法: 用阅读顺序解释为什么y轴向下(看书从第一行开始,往下看)
  • 对比记忆: 在纸上画两个坐标系对比,加深印象
  • 实际演示: 在纸上画图,然后思考在计算机上如何实现
  • 常见错误: 孩子容易混淆两个坐标系,需要多次强调“左上角是原点“

16.2.3 窗口的坐标和尺寸

让我们用代码理解窗口的坐标和尺寸:

import tkinter as tk

root = tk.Tk()
root.title("窗口坐标示例")

# 设置窗口大小(宽x高)
root.geometry("400x300")

print(f"窗口宽度: {root.winfo_width()}")  # 400
print(f"窗口高度: {root.winfo_height()}")  # 300

# 注意:窗口创建后,需要更新才能获取正确尺寸
root.update()
print(f"更新后宽度: {root.winfo_width()}")
print(f"更新后高度: {root.winfo_height()}")

root.mainloop()

代码解析:

  • geometry("400x300"): 设置窗口宽400像素,高300像素
  • winfo_width(): 获取窗口的当前宽度
  • winfo_height(): 获取窗口的当前高度
  • update(): 强制窗口更新,确保获取到正确的尺寸

16.2.4 组件的位置坐标

当我们添加按钮、标签等组件时,需要指定它们的位置。

位置参数:

  • x: 组件左上角的x坐标(从窗口左边开始)
  • y: 组件左上角的y坐标(从窗口上边开始)
  • width: 组件的宽度
  • height: 组件的高度

示例:

import tkinter as tk

root = tk.Tk()
root.title("组件位置示例")
root.geometry("400x300")

# 添加一个标签,指定位置(x=50, y=100)
label = tk.Label(root, text="我在哪里?", bg="yellow")
label.place(x=50, y=100, width=200, height=30)

root.mainloop()

代码解析:

  • x=50: 标签左边距离窗口左边50像素
  • y=100: 标签上边距离窗口上边100像素
  • width=200: 标签宽度200像素
  • height=30: 标签高度30像素

可视化:

窗口(400x300)
┌────────────────────────────┐
│ ↑y                        │
│ │                        │
│ │  ┌──────────┐           │
│100  │黄色标签  │(200x30) │
│ │  └──────────┘           │
│ │                        │
│ └─→x                      │
│ 50                        │
└────────────────────────────┘

16.2.5 坐标计算练习

让我们做一些数学练习,巩固坐标的概念!

练习1: 计算组件位置

窗口大小是600×400,你想把一个按钮放在:

  • 窗口的正中央
  • 按钮大小是100×50

数学计算:

窗口宽度 = 600,窗口高度 = 400
按钮宽度 = 100,按钮高度 = 50

正中央的x坐标 = (窗口宽度 - 按钮宽度) / 2
                = (600 - 100) / 2
                = 500 / 2
                = 250

正中央的y坐标 = (窗口高度 - 按钮高度) / 2
                = (400 - 50) / 2
                = 350 / 2
                = 175

所以按钮坐标是 (250, 175)

Python代码验证:

import tkinter as tk

root = tk.Tk()
root.title("居中按钮")
root.geometry("600x400")

# 计算居中位置
window_width = 600
window_height = 400
button_width = 100
button_height = 50

center_x = (window_width - button_width) / 2  # 250
center_y = (window_height - button_height) / 2  # 175

# 创建按钮
button = tk.Button(root, text="我在正中央!", bg="lightblue")
button.place(x=center_x, y=center_y, width=button_width, height=button_height)

print(f"按钮坐标: ({center_x}, {center_y})")

root.mainloop()

练习2: 计算窗口面积和组件面积

import tkinter as tk

root = tk.Tk()
root.title("面积计算")
root.geometry("500x400")

# 窗口面积
window_area = 500 * 400
print(f"窗口面积: {window_area} 平方像素")

# 添加3个按钮
btn1 = tk.Button(root, text="按钮1", bg="red")
btn1.place(x=50, y=50, width=100, height=40)

btn2 = tk.Button(root, text="按钮2", bg="green")
btn2.place(x=200, y=100, width=120, height=50)

btn3 = tk.Button(root, text="按钮3", bg="blue")
btn3.place(x=100, y=250, width=150, height=60)

# 计算按钮总面积
btn1_area = 100 * 40  # 4000
btn2_area = 120 * 50  # 6000
btn3_area = 150 * 60  # 9000
total_btn_area = btn1_area + btn2_area + btn3_area

print(f"按钮1面积: {btn1_area}")
print(f"按钮2面积: {btn2_area}")
print(f"按钮3面积: {btn3_area}")
print(f"按钮总面积: {total_btn_area}")
print(f"按钮占窗口比例: {total_btn_area / window_area * 100:.1f}%")

root.mainloop()

运行结果:

窗口面积: 200000 平方像素
按钮1面积: 4000
按钮2面积: 6000
按钮3面积: 9000
按钮总面积: 19000
按钮占窗口比例: 9.5%

👨‍🏫 给家长的小贴士

  • 数学融入: GUI编程是复习数学知识(坐标、面积、比例)的好机会
  • 可视化: 比起纸上的题目,GUI让学生能直观看到计算结果
  • 实际应用: 问孩子“如果让你设计一个登录界面,按钮应该放哪里?“
  • 难度递进: 从简单的位置计算开始,逐步到复杂的布局
  • 鼓励调试: 让孩子修改坐标数值,看看组件位置如何变化

16.2.6 坐标系对比总结

特性数学坐标系计算机坐标系
原点位置左下角左上角
x轴方向向右为正向右为正
y轴方向向上为正向下为正
坐标表示(x, y)(x, y)
常见用途数学函数图像窗口、网页布局

🎯 实践练习2:坐标计算练习

任务: 创建一个窗口(600×400),完成以下组件的放置:

  1. 标签“标题“:放在窗口正上方(x=200, y=20,宽200,高30)
  2. 按钮“确定“:放在窗口正中央,大小100×40
  3. 按钮“取消“:放在“确定“按钮右边50像素处
  4. 计算“确定“按钮的坐标(x, y)
  5. 计算两个按钮总共占多少面积

答案:

🔍 查看答案
import tkinter as tk

root = tk.Tk()
root.title("坐标练习")
root.geometry("600x400")

# 1. 标题
title_label = tk.Label(root, text="用户登录", bg="lightyellow", font=("Arial", 16))
title_label.place(x=200, y=20, width=200, height=30)

# 2. 确定按钮(正中央)
btn_width = 100
btn_height = 40
window_width = 600
window_height = 400

ok_x = (window_width - btn_width) / 2  # 250
ok_y = (window_height - btn_height) / 2  # 180

ok_btn = tk.Button(root, text="确定", bg="lightgreen")
ok_btn.place(x=ok_x, y=ok_y, width=btn_width, height=btn_height)

print(f"确定按钮坐标: ({ok_x}, {ok_y})")

# 3. 取消按钮(右边50像素)
cancel_x = ok_x + btn_width + 50  # 250 + 100 + 50 = 400
cancel_y = ok_y  # 和确定按钮同一行

cancel_btn = tk.Button(root, text="取消", bg="lightcoral")
cancel_btn.place(x=cancel_x, y=cancel_y, width=btn_width, height=btn_height)

# 4. 计算总面积
one_btn_area = btn_width * btn_height  # 4000
two_btn_area = one_btn_area * 2  # 8000
print(f"两个按钮总面积: {two_btn_area} 平方像素")

root.mainloop()

16.3 常用组件:按钮、标签和输入框

窗口创建好了,但里面空空如也!让我们添加一些组件(Widget)。

什么是组件? 组件就是窗口里的各种元素,比如:

  • 按钮(Button)
  • 标签(Label,显示文字)
  • 输入框(Entry)
  • 文本框(Text)
  • 等等……

16.2.1 标签(Label)- 显示文字

标签用来在窗口上显示文字或图片。

import tkinter as tk

root = tk.Tk()
root.title("标签示例")
root.geometry("300x200")

# 创建一个标签
label = tk.Label(root, text="你好,这是我的第一个标签!")

# 把标签显示到窗口上
label.pack()

root.mainloop()

运行结果: 窗口中显示了一行文字:“你好,这是我的第一个标签!”

代码解释:

  1. tk.Label(root, text="...")

    • root:表示这个标签要放在哪个窗口里
    • text="...":标签上显示的文字
  2. label.pack()

    • 这一行很重要!
    • 把标签“打包“放到窗口上
    • 如果不写这一行,标签不会显示

🎯 类比时间

想象你在布置房间:

  • tk.Label():在纸上画出装饰品
  • .pack():把这个装饰品真正贴到墙上

16.2.2 按钮(Button)- 可以点击

按钮是最常用的组件之一,点击后可以触发某个操作。

import tkinter as tk

root = tk.Tk()
root.title("按钮示例")
root.geometry("300x200")

# 创建一个按钮
button = tk.Button(root, text="点击我")

# 把按钮显示到窗口上
button.pack()

root.mainloop()

运行结果: 窗口中显示了一个按钮,上面写着“点击我“,你可以用鼠标点击它!(虽然现在点击不会发生什么)

16.2.3 输入框(Entry)- 输入文字

输入框让用户可以输入文字。

import tkinter as tk

root = tk.Tk()
root.title("输入框示例")
root.geometry("300x200")

# 创建一个标签作为提示
label = tk.Label(root, text="请输入你的名字:")
label.pack()

# 创建一个输入框
entry = tk.Entry(root)
entry.pack()

root.mainloop()

运行结果:

  • 上面一行文字:“请输入你的名字:”
  • 下面一个白色的输入框,你可以在里面打字!

16.2.4 让按钮“动“起来:事件处理

现在按钮能点击,但点击后什么都没发生。让我们让按钮点击后做点事情!

import tkinter as tk

# 定义一个函数(点击按钮时会执行这个函数)
def on_button_click():
    print("按钮被点击了!")

root = tk.Tk()
root.title("按钮点击示例")
root.geometry("300x200")

# 创建按钮,点击时执行on_button_click函数
button = tk.Button(root, text="点击我", command=on_button_click)
button.pack()

root.mainloop()

运行结果: 每次点击按钮,在命令行(终端/黑窗口)中会输出:“按钮被点击了!”

代码解释:

  1. def on_button_click():

    • 定义了一个函数
    • 这个函数包含了点击按钮时要执行的代码
  2. command=on_button_click

    • command是Button的一个参数
    • 它告诉按钮:被点击时执行哪个函数
    • 注意:函数名后面不要加括号()

⚠️ 重要提醒

# ✅ 正确写法
button = tk.Button(root, text="点击我", command=on_button_click)

# ❌ 错误写法
button = tk.Button(root, text="点击我", command=on_button_click())

为什么不能加括号?

  • 加了括号意味着“立刻执行这个函数“
  • 不加括号意味着“把这个函数的名字告诉按钮,等点击时再执行“

16.2.5 实用示例:获取输入框的内容

让我们做一个更有用的例子:在输入框输入名字,点击按钮后显示问候语!

import tkinter as tk

def say_hello():
    # 获取输入框的内容
    name = entry.get()

    # 创建一个新标签显示问候语
    result_label = tk.Label(root, text=f"你好,{name}!")
    result_label.pack()

root = tk.Tk()
root.title("问候程序")
root.geometry("300x200")

# 提示标签
label = tk.Label(root, text="请输入你的名字:")
label.pack()

# 输入框
entry = tk.Entry(root)
entry.pack()

# 按钮
button = tk.Button(root, text="问候", command=say_hello)
button.pack()

root.mainloop()

运行结果:

  1. 在输入框输入名字,比如“小明“
  2. 点击“问候“按钮
  3. 下面会显示:“你好,小明!”

代码解释:

  1. entry.get()

    • 获取输入框中用户输入的内容
    • 返回一个字符串
  2. 每次点击按钮,都会创建一个新标签显示问候语

🎮 实践练习2:制作一个简单的计算器

任务:制作一个能计算两个数之和的程序

要求

  • 两个输入框:输入第一个数和第二个数
  • 一个按钮:点击时计算和
  • 一个标签:显示计算结果

提示

  • 使用两个Entry组件
  • 使用entry.get()获取输入
  • 注意get()返回的是字符串,需要用int()转换为数字
🔍 查看答案
import tkinter as tk

def calculate_sum():
    # 获取两个输入框的值
    num1 = entry1.get()
    num2 = entry2.get()

    # 转换为整数并计算
    result = int(num1) + int(num2)

    # 显示结果
    result_label.config(text=f"结果:{result}")

root = tk.Tk()
root.title("加法计算器")
root.geometry("300x200")

# 第一个数
label1 = tk.Label(root, text="第一个数:")
label1.pack()
entry1 = tk.Entry(root)
entry1.pack()

# 第二个数
label2 = tk.Label(root, text="第二个数:")
label2.pack()
entry2 = tk.Entry(root)
entry2.pack()

# 计算按钮
button = tk.Button(root, text="计算", command=calculate_sum)
button.pack()

# 结果标签
result_label = tk.Label(root, text="结果:")
result_label.pack()

root.mainloop()

进阶挑战

  • 如果用户输入的不是数字,程序会出错吗?如何改进?
  • 提示:使用try-except捕获错误

16.3 布局管理:让组件排列整齐

现在你会发现一个问题:每次用.pack(),组件都是自动从上往下排。如果我们想要更灵活的布局呢?

tkinter提供了三种布局管理器:

  1. pack:按顺序排列(简单)
  2. grid:网格排列(灵活)
  3. place:精确位置(精确)

16.3.1 pack布局 - 简单排列

pack是最简单的布局方式,组件会按顺序从上到下、从左到右排列。

import tkinter as tk

root = tk.Tk()
root.title("pack布局示例")
root.geometry("300x200")

label1 = tk.Label(root, text="第一个标签")
label1.pack()

label2 = tk.Label(root, text="第二个标签")
label2.pack()

label3 = tk.Label(root, text="第三个标签")
label3.pack()

root.mainloop()

运行结果: 三个标签从上到下排列

pack的常用参数:

# 从顶部开始排列
label.pack(side="top")

# 从底部开始排列
label.pack(side="bottom")

# 从左边开始排列
label.pack(side="left")

# 从右边开始排列
label.pack(side="right")

# 设置外部间距(与其他组件的距离)
label.pack(padx=10, pady=10)  # x方向10像素,y方向10像素

# 设置内部填充(组件内容与边框的距离)
label.pack(ipadx=5, ipady=5)

16.3.2 grid布局 - 网格排列

grid让窗口像一个表格,可以指定组件放在哪一行、哪一列。

import tkinter as tk

root = tk.Tk()
root.title("grid布局示例")
root.geometry("300x200")

# 第1行,第1列
label1 = tk.Label(root, text="用户名:")
label1.grid(row=0, column=0)

# 第1行,第2列
entry1 = tk.Entry(root)
entry1.grid(row=0, column=1)

# 第2行,第1列
label2 = tk.Label(root, text="密码:")
label2.grid(row=1, column=0)

# 第2行,第2列
entry2 = tk.Entry(root)
entry2.grid(row=1, column=1)

# 第3行,跨两列
button = tk.Button(root, text="登录")
button.grid(row=2, column=0, columnspan=2)

root.mainloop()

运行结果:

用户名: [输入框]
密码:   [输入框]
[     登录按钮     ]

代码解释:

  1. row=0, column=0:放在第0行、第0列(注意:从0开始计数!)
  2. columnspan=2:跨2列(按钮横跨两列)

💡 什么时候用pack,什么时候用grid?

用pack

  • 简单的从上到下排列
  • 不需要精确控制位置

用grid

  • 需要表格式的布局
  • 需要精确控制行和列
  • 比如登录表单(标签和输入框对齐)

🎮 实践练习3:使用grid布局制作登录界面

任务:使用grid布局制作一个登录界面

要求

  • 用户名标签和输入框(第1行)
  • 密码标签和输入框(第2行)
  • 登录按钮(第3行,跨两列居中)
🔍 查看答案
import tkinter as tk

def login():
    username = entry_username.get()
    password = entry_password.get()
    result_label.config(text=f"用户名:{username}\n密码:{password}")

root = tk.Tk()
root.title("登录界面")
root.geometry("300x200")

# 用户名
label_username = tk.Label(root, text="用户名:")
label_username.grid(row=0, column=0, padx=5, pady=5)
entry_username = tk.Entry(root)
entry_username.grid(row=0, column=1, padx=5, pady=5)

# 密码
label_password = tk.Label(root, text="密码:")
label_password.grid(row=1, column=0, padx=5, pady=5)
entry_password = tk.Entry(root, show="*")  # 显示为星号
entry_password.grid(row=1, column=1, padx=5, pady=5)

# 登录按钮
button_login = tk.Button(root, text="登录", command=login)
button_login.grid(row=2, column=0, columnspan=2, pady=10)

# 结果标签
result_label = tk.Label(root, text="")
result_label.grid(row=3, column=0, columnspan=2)

root.mainloop()

新知识点

  • show="*":让输入框显示为星号(用于密码输入)
  • padx=5, pady=5:设置组件的外部间距

16.3.3 place布局 - 精确定位

place可以精确指定组件的位置(不常用,了解一下即可)。

import tkinter as tk

root = tk.Tk()
root.title("place布局示例")
root.geometry("300x200")

# 在坐标(100, 50)处放置标签
label = tk.Label(root, text="精确位置的标签")
label.place(x=100, y=50)

root.mainloop()

16.4 更多常用组件

除了按钮、标签和输入框,tkinter还有很多其他有用的组件。

16.4.1 文本框(Text)- 多行文本

**输入框(Entry)**只能输入一行,**文本框(Text)**可以输入多行。

import tkinter as tk

def show_text():
    # 获取文本框内容
    content = text.get("1.0", "end")  # 从第1行第0列到末尾
    print(content)

root = tk.Tk()
root.title("文本框示例")
root.geometry("400x300")

# 创建文本框
text = tk.Text(root, width=40, height=10)
text.pack()

# 创建按钮
button = tk.Button(root, text="显示内容", command=show_text)
button.pack()

root.mainloop()

运行结果:

  • 一个大的文本框,可以输入多行文字
  • 点击按钮后,在终端显示文本框内容

代码解释:

  • width=40, height=10:文本框宽40个字符,高10行
  • text.get("1.0", "end"):获取从第1行第0列到末尾的所有内容

16.4.2 复选框(Checkbutton)

复选框让用户选择“是/否“或“选中/不选中“。

import tkinter as tk

def show_hobbies():
    if var_music.get() == 1:
        print("你喜欢音乐")
    if var_sports.get() == 1:
        print("你喜欢运动")
    if var_reading.get() == 1:
        print("你喜欢阅读")

root = tk.Tk()
root.title("复选框示例")
root.geometry("300x200")

# 创建变量存储复选框状态
var_music = tk.IntVar()
var_sports = tk.IntVar()
var_reading = tk.IntVar()

# 创建复选框
check_music = tk.Checkbutton(root, text="音乐", variable=var_music)
check_music.pack()

check_sports = tk.Checkbutton(root, text="运动", variable=var_sports)
check_sports.pack()

check_reading = tk.Checkbutton(root, text="阅读", variable=var_reading)
check_reading.pack()

# 按钮
button = tk.Button(root, text="确定", command=show_hobbies)
button.pack()

root.mainloop()

代码解释:

  • tk.IntVar():创建一个整数变量
  • variable=var_music:把复选框和变量绑定
  • var_music.get() == 1:如果复选框被选中,值为1,否则为0

16.4.3 单选按钮(Radiobutton)

单选按钮让用户从多个选项中选一个

import tkinter as tk

def show_grade():
    grade = var_grade.get()
    print(f"你选择的年级是:{grade}")

root = tk.Tk()
root.title("单选按钮示例")
root.geometry("300x200")

# 创建变量存储选项
var_grade = tk.StringVar()
var_grade.set("一年级")  # 默认值

# 创建单选按钮
radio1 = tk.Radiobutton(root, text="一年级", variable=var_grade, value="一年级")
radio1.pack()

radio2 = tk.Radiobutton(root, text="二年级", variable=var_grade, value="二年级")
radio2.pack()

radio3 = tk.Radiobutton(root, text="三年级", variable=var_grade, value="三年级")
radio3.pack()

# 按钮
button = tk.Button(root, text="确定", command=show_grade)
button.pack()

root.mainloop()

代码解释:

  • tk.StringVar():创建一个字符串变量
  • value="一年级":这个选项代表的值
  • 所有单选按钮使用同一个variable,这样它们就会互斥(只能选一个)

🎮 实践练习4:制作调查问卷

任务:制作一个调查问卷

要求

  • 姓名(输入框)
  • 年级(单选按钮:一年级/二年级/三年级/四年级/五年级/六年级)
  • 爱好(复选框:阅读/运动/音乐/绘画/游戏)
  • 提交按钮(点击后显示用户的选择)
🔍 查看答案
import tkinter as tk

def submit():
    name = entry_name.get()
    grade = var_grade.get()

    hobbies = []
    if var_reading.get() == 1:
        hobbies.append("阅读")
    if var_sports.get() == 1:
        hobbies.append("运动")
    if var_music.get() == 1:
        hobbies.append("音乐")
    if var_painting.get() == 1:
        hobbies.append("绘画")
    if var_games.get() == 1:
        hobbies.append("游戏")

    result = f"姓名:{name}\n年级:{grade}\n爱好:{', '.join(hobbies)}"
    result_label.config(text=result)

root = tk.Tk()
root.title("调查问卷")
root.geometry("400x500")

# 姓名
label_name = tk.Label(root, text="姓名:")
label_name.pack(pady=5)
entry_name = tk.Entry(root)
entry_name.pack(pady=5)

# 年级
label_grade = tk.Label(root, text="年级:")
label_grade.pack(pady=5)
var_grade = tk.StringVar()
var_grade.set("一年级")
grades = ["一年级", "二年级", "三年级", "四年级", "五年级", "六年级"]
for g in grades:
    radio = tk.Radiobutton(root, text=g, variable=var_grade, value=g)
    radio.pack()

# 爱好
label_hobbies = tk.Label(root, text="爱好(可多选):")
label_hobbies.pack(pady=5)

var_reading = tk.IntVar()
var_sports = tk.IntVar()
var_music = tk.IntVar()
var_painting = tk.IntVar()
var_games = tk.IntVar()

check_reading = tk.Checkbutton(root, text="阅读", variable=var_reading)
check_reading.pack()
check_sports = tk.Checkbutton(root, text="运动", variable=var_sports)
check_sports.pack()
check_music = tk.Checkbutton(root, text="音乐", variable=var_music)
check_music.pack()
check_painting = tk.Checkbutton(root, text="绘画", variable=var_painting)
check_painting.pack()
check_games = tk.Checkbutton(root, text="游戏", variable=var_games)
check_games.pack()

# 提交按钮
button_submit = tk.Button(root, text="提交", command=submit)
button_submit.pack(pady=10)

# 结果
result_label = tk.Label(root, text="")
result_label.pack(pady=10)

root.mainloop()

新知识点

  • 使用循环创建多个单选按钮
  • ', '.join(hobbies):把列表用逗号连接成字符串

16.5 综合项目:改写命令行程序为GUI版本

现在我们已经学习了tkinter的基本知识,让我们把之前的命令行程序改写成图形界面版本!

16.5.1 项目选择:猜数字游戏

你还记得第7章(条件语句)中的猜数字游戏吗?

原版(命令行版本):

import random

secret = random.randint(1, 100)
guess = 0

while guess != secret:
    guess = int(input("猜一个1-100的数字:"))
    if guess > secret:
        print("太大了!")
    elif guess < secret:
        print("太小了!")
    else:
        print("恭喜你,猜对了!")

改版(GUI版本):

import random
import tkinter as tk
from tkinter import messagebox

def check_guess():
    # 获取用户输入
    user_input = entry_guess.get()

    # 检查是否输入了数字
    if not user_input:
        messagebox.showwarning("提示", "请输入一个数字!")
        return

    try:
        guess = int(user_input)
    except ValueError:
        messagebox.showerror("错误", "请输入有效的数字!")
        return

    # 检查数字范围
    if guess < 1 or guess > 100:
        messagebox.showwarning("提示", "请输入1-100之间的数字!")
        return

    # 检查猜测结果
    global attempts
    attempts += 1

    if guess > secret:
        result_label.config(text="太大了!再试一次", fg="red")
    elif guess < secret:
        result_label.config(text="太小了!再试一次", fg="blue")
    else:
        result_label.config(text=f"恭喜你猜对了!答案就是{secret}!\n你用了{attempts}次。", fg="green")
        messagebox.showinfo("成功", f"恭喜你猜对了!\n答案:{secret}\n尝试次数:{attempts}")
        restart_game()

def restart_game():
    global secret, attempts
    secret = random.randint(1, 100)
    attempts = 0
    result_label.config(text="游戏已重新开始!请猜1-100的数字", fg="black")
    entry_guess.delete(0, tk.END)

# 初始化游戏
secret = random.randint(1, 100)
attempts = 0

# 创建主窗口
root = tk.Tk()
root.title("猜数字游戏")
root.geometry("400x300")

# 标题
title_label = tk.Label(root, text="猜数字游戏(1-100)", font=("Arial", 16))
title_label.pack(pady=10)

# 说明
instruction_label = tk.Label(root, text="我已经想好了一个1-100的数字,你来猜!")
instruction_label.pack(pady=5)

# 输入框
entry_guess = tk.Entry(root, font=("Arial", 12))
entry_guess.pack(pady=10)
entry_guess.focus()  # 让输入框自动获得焦点

# 按钮
button_guess = tk.Button(root, text="猜一下", command=check_guess, font=("Arial", 12))
button_guess.pack(pady=5)

button_restart = tk.Button(root, text="重新开始", command=restart_game, font=("Arial", 12))
button_restart.pack(pady=5)

# 结果标签
result_label = tk.Label(root, text="请输入1-100的数字", font=("Arial", 12))
result_label.pack(pady=10)

root.mainloop()

运行效果:

  1. 窗口标题:“猜数字游戏”
  2. 显示说明:“我已经想好了一个1-100的数字,你来猜!”
  3. 输入框:输入猜测的数字
  4. “猜一下“按钮:点击后检查猜测结果
  5. “重新开始“按钮:开始新游戏
  6. 结果标签:显示“太大了“、“太小了“或“恭喜你猜对了”

新知识点:

  1. messagebox.showwarning("标题", "消息")

    • 显示警告对话框
    • 有黄色的警告图标
  2. messagebox.showerror("标题", "消息")

    • 显示错误对话框
    • 有红色的错误图标
  3. messagebox.showinfo("标题", "消息")

    • 显示信息对话框
    • 有蓝色的信息图标
  4. entry_guess.delete(0, tk.END)

    • 清空输入框的内容
    • 0:从第0个字符开始
    • tk.END:到末尾
  5. entry_guess.focus()

    • 让输入框自动获得焦点
    • 程序启动后可以直接输入,不需要先点击输入框
  6. result_label.config(text="...", fg="red")

    • 修改标签的文字和颜色
    • fg是foreground(前景色)的缩写

16.5.2 更多改进思路

还可以继续改进这个游戏:

  1. 添加进度提示

    # 显示还剩多少次机会
    attempts_left_label = tk.Label(root, text=f"剩余机会:{max_attempts - attempts}")
    attempts_left_label.pack()
    
  2. 添加历史记录

    # 记录之前的猜测
    history_label = tk.Label(root, text="历史记录:" + ", ".join(history))
    history_label.pack()
    
  3. 添加难度选择

    # 简单:1-50,普通:1-100,困难:1-200
    

🎮 实践练习5:改写其他CLI程序

任务:选择一个之前写过的命令行程序,改写成GUI版本

建议选择

  • 计算器(第6章顺序语句)
  • 成绩等级判断(第7章条件语句)
  • 乘法口诀表(第8章循环语句)
  • 简单计算器(第12章函数)

要求

  • 使用至少3种不同的组件
  • 合理布局(美观、易用)
  • 添加错误处理(输入非数字等)
  • 添加说明文字

提示

  • 先画草图:界面怎么设计
  • 列出需要的组件
  • 选择合适的布局方式(pack或grid)
  • 逐步添加功能,不要一次写完

16.6 美化界面

现在我们的程序功能已经完整,但界面还可以更漂亮!

16.6.1 设置字体和颜色

import tkinter as tk

root = tk.Tk()
root.title("美化界面")
root.geometry("400x300")

# 大标题
title_label = tk.Label(
    root,
    text="欢迎来到Python世界",
    font=("Arial", 20, "bold"),  # 字体、大小、加粗
    fg="blue",  # 前景色(文字颜色)
    bg="yellow"  # 背景色
)
title_label.pack(pady=20)

# 按钮
button = tk.Button(
    root,
    text="点击我",
    font=("宋体", 14),
    bg="green",
    fg="white",
    activebackground="darkgreen",  # 点击时的背景色
    activeforeground="white",  # 点击时的文字颜色
    relief="raised",  # 边框样式:raised(凸起)、sunken(凹陷)、flat(扁平)
    bd=3  # 边框宽度
)
button.pack(pady=10)

root.mainloop()

运行效果:

  • 黄色背景、蓝色粗体大标题
  • 绿色按钮(白色文字)
  • 点击按钮时变成深绿色

16.6.2 常用颜色

tkinter支持多种颜色设置方式:

# 1. 颜色名称
fg="red"
fg="blue"
fg="green"
fg="yellow"
fg="black"
fg="white"

# 2. 十六进制颜色码(更精确)
fg="#FF0000"  # 红色
fg="#0000FF"  # 蓝色
fg="#00FF00"  # 绿色
fg="#FFFF00"  # 黄色

常用颜色速查表:

颜色名称十六进制
红色red#FF0000
绿色green#008000
蓝色blue#0000FF
黄色yellow#FFFF00
黑色black#000000
白色white#FFFFFF
橙色orange#FFA500
紫色purple#800080

16.6.3 设置组件大小

# 设置标签的大小(宽度和高度)
label = tk.Label(
    root,
    text="固定大小的标签",
    width=20,  # 宽度(字符数)
    height=3   # 高度(行数)
)

# 设置按钮的大小
button = tk.Button(
    root,
    text="固定大小的按钮",
    width=15,
    height=2
)

🎨 实践练习6:美化猜数字游戏

任务:美化之前制作的猜数字游戏

要求

  • 标题使用大字体、加粗、蓝色
  • 按钮使用绿色背景、白色文字
  • 输入框使用大字体
  • 结果文字根据情况显示不同颜色(太大=红色,太小=蓝色,正确=绿色)
  • 添加背景色
🔍 查看答案
import random
import tkinter as tk
from tkinter import messagebox

def check_guess():
    user_input = entry_guess.get()

    if not user_input:
        messagebox.showwarning("提示", "请输入一个数字!")
        return

    try:
        guess = int(user_input)
    except ValueError:
        messagebox.showerror("错误", "请输入有效的数字!")
        return

    if guess < 1 or guess > 100:
        messagebox.showwarning("提示", "请输入1-100之间的数字!")
        return

    global attempts
    attempts += 1

    if guess > secret:
        result_label.config(text="太大了!再试一次", fg="red")
    elif guess < secret:
        result_label.config(text="太小了!再试一次", fg="blue")
    else:
        result_label.config(text=f"恭喜你猜对了!答案就是{secret}!\n你用了{attempts}次。", fg="green")
        messagebox.showinfo("成功", f"恭喜你猜对了!\n答案:{secret}\n尝试次数:{attempts}")
        restart_game()

def restart_game():
    global secret, attempts
    secret = random.randint(1, 100)
    attempts = 0
    result_label.config(text="游戏已重新开始!请猜1-100的数字", fg="black")
    entry_guess.delete(0, tk.END)

# 初始化游戏
secret = random.randint(1, 100)
attempts = 0

# 创建主窗口
root = tk.Tk()
root.title("猜数字游戏")
root.geometry("500x400")
root.configure(bg="#f0f0f0")  # 设置窗口背景色

# 标题
title_label = tk.Label(
    root,
    text="🎮 猜数字游戏(1-100)",
    font=("Arial", 20, "bold"),
    fg="blue",
    bg="#f0f0f0"
)
title_label.pack(pady=15)

# 说明
instruction_label = tk.Label(
    root,
    text="我已经想好了一个1-100的数字,你来猜!",
    font=("Arial", 12),
    bg="#f0f0f0"
)
instruction_label.pack(pady=5)

# 输入框
entry_guess = tk.Entry(root, font=("Arial", 14))
entry_guess.pack(pady=10)
entry_guess.focus()

# 按钮框架
button_frame = tk.Frame(root, bg="#f0f0f0")
button_frame.pack(pady=10)

button_guess = tk.Button(
    button_frame,
    text="🔍 猜一下",
    command=check_guess,
    font=("Arial", 12),
    bg="green",
    fg="white",
    width=10
)
button_guess.grid(row=0, column=0, padx=5)

button_restart = tk.Button(
    button_frame,
    text="🔄 重新开始",
    command=restart_game,
    font=("Arial", 12),
    bg="orange",
    fg="white",
    width=10
)
button_restart.grid(row=0, column=1, padx=5)

# 结果标签
result_label = tk.Label(
    root,
    text="请输入1-100的数字",
    font=("Arial", 14),
    bg="#f0f0f0"
)
result_label.pack(pady=15)

root.mainloop()

16.7 常见错误和调试

在编写GUI程序时,可能会遇到各种问题。让我们看看常见的错误和解决方法。

16.7.1 常见错误

错误1:窗口不显示

错误代码:

import tkinter as tk

root = tk.Tk()
# 忘记写 root.mainloop()

原因:忘记调用mainloop()

解决方法:在代码最后加上root.mainloop()


错误2:组件不显示

错误代码:

import tkinter as tk

root = tk.Tk()
label = tk.Label(root, text="你好")
# 忘记写 label.pack() 或 label.grid()
root.mainloop()

原因:创建组件后没有使用布局管理器(pack、grid或place)

解决方法:在创建组件后调用布局方法

label = tk.Label(root, text="你好")
label.pack()  # 或 label.grid(...)

错误3:组件重叠

错误代码:

import tkinter as tk

root = tk.Tk()

# 同时使用pack和grid
label1 = tk.Label(root, text="标签1")
label1.pack()

label2 = tk.Label(root, text="标签2")
label2.grid(row=0, column=0)

root.mainloop()

错误提示:

_tkinter.TclError: cannot use geometry manager pack inside .!frame which already has slaves managed by grid

原因:在同一个父容器中混用pack和grid

解决方法:在同一个父容器中只使用一种布局方式

# 全部使用pack
label1.pack()
label2.pack()

# 或者全部使用grid
label1.grid(row=0, column=0)
label2.grid(row=1, column=0)

错误4:获取输入框内容时出错

错误代码:

def get_input():
    # 用户没有输入任何内容
    text = entry.get()
    number = int(text)  # 如果text是空字符串,会出错

错误提示:

ValueError: invalid literal for int() with base 10: ''

原因:输入框为空时,get()返回空字符串,无法转换为数字

解决方法:先检查是否为空

def get_input():
    text = entry.get()

    # 检查是否为空
    if not text:
        messagebox.showwarning("提示", "请输入内容!")
        return

    number = int(text)

错误5:函数调用时参数错误

错误代码:

def say_hello(name):
    print(f"你好,{name}!")

button = tk.Button(root, text="点击", command=say_hello("小明"))

错误现象:程序启动时就调用了函数,而不是点击时调用

原因command=say_hello("小明")会立刻执行函数,正确的做法是传递函数名

解决方法:使用lambda表达式

# 方法1:使用lambda
button = tk.Button(root, text="点击", command=lambda: say_hello("小明"))

# 方法2:改写函数,不使用参数
def say_hello():
    name = entry.get()
    print(f"你好,{name}!")

button = tk.Button(root, text="点击", command=say_hello)

16.7.2 调试技巧

技巧1:使用print调试

在关键位置添加print语句,查看程序执行情况

def check_guess():
    guess = entry.get()
    print(f"用户输入:{guess}")  # 调试输出

    if int(guess) > secret:
        print("猜大了")  # 调试输出
        result_label.config(text="太大了")

技巧2:使用messagebox显示中间结果

def check_guess():
    guess = entry.get()
    messagebox.showinfo("调试", f"用户输入:{guess}")
    # 后续代码...

技巧3:逐步添加功能

不要一次写完所有代码,而是逐步添加功能,每步都测试

# 第1步:先创建窗口
root = tk.Tk()
root.mainloop()

# 第2步:添加标签
root = tk.Tk()
label = tk.Label(root, text="你好")
label.pack()
root.mainloop()

# 第3步:添加按钮
root = tk.Tk()
label = tk.Label(root, text="你好")
label.pack()
button = tk.Button(root, text="点击")
button.pack()
root.mainloop()

# 第4步:添加功能
def on_click():
    print("点击了")

button = tk.Button(root, text="点击", command=on_click)

技巧4:注释掉部分代码

如果程序出错,可以注释掉部分代码,逐步缩小问题范围

def complex_function():
    # 注释掉复杂的部分
    # result = some_calculation()
    # another_function(result)

    # 先测试简单部分
    print("函数被调用")

🎮 实践练习7:调试练习

任务:找出下面代码中的5个错误并修正

import tkinter as tk

def show_info():
    name = entry.get()
    age = int(age_entry.get())
    print(f"姓名:{name},年龄:{age}")

root = tk.Tk()
root.title("信息输入")

label = tk.Label(root, text="姓名:")
label.pack()
entry = tk.Entry(root)
entry.grid(row=0, column=1)  # 错误1

age_label = tk.Label(root, text="年龄:")
age_label.pack()
age_entry = tk.Entry(root)
age_entry.pack()

button = tk.Button(root, text="提交", command=show_info())  # 错误2
button.pack()

# 错误3:忘记mainloop()
🔍 查看答案

错误1:混用pack和grid布局

# 错误
label.pack()
entry.grid(row=0, column=1)

# 正确:统一使用pack
label.pack()
entry.pack()

# 或统一使用grid
label.grid(row=0, column=0)
entry.grid(row=0, column=1)

错误2:command参数不应该加括号

# 错误
button = tk.Button(root, text="提交", command=show_info())

# 正确
button = tk.Button(root, text="提交", command=show_info)

错误3:忘记mainloop()

# 正确
root.mainloop()

额外问题

  • 如果age_entry为空,int(age_entry.get())会报错
  • 应该添加错误处理

完整正确代码

import tkinter as tk
from tkinter import messagebox

def show_info():
    name = entry.get()
    age_text = age_entry.get()

    if not name or not age_text:
        messagebox.showwarning("提示", "请填写完整信息!")
        return

    try:
        age = int(age_text)
        result_label.config(text=f"姓名:{name},年龄:{age}")
    except ValueError:
        messagebox.showerror("错误", "年龄必须是数字!")

root = tk.Tk()
root.title("信息输入")
root.geometry("300x200")

label = tk.Label(root, text="姓名:")
label.pack()
entry = tk.Entry(root)
entry.pack()

age_label = tk.Label(root, text="年龄:")
age_label.pack()
age_entry = tk.Entry(root)
age_entry.pack()

button = tk.Button(root, text="提交", command=show_info)
button.pack()

result_label = tk.Label(root, text="")
result_label.pack()

root.mainloop()

16.8 本章小结

16.8.1 核心知识点回顾

1. GUI vs CLI

  • GUI(图形用户界面):有窗口、按钮、颜色,用鼠标操作
  • CLI(命令行界面):只有文字,用键盘输入命令

2. tkinter库

  • Python自带的GUI库
  • 可以创建窗口、按钮、输入框等组件
  • 使用root = tk.Tk()创建主窗口
  • 使用root.mainloop()让窗口持续显示

3. 常用组件

组件类名用途
标签Label显示文字或图片
按钮Button点击后执行操作
输入框Entry输入单行文字
文本框Text输入多行文字
复选框Checkbutton多选
单选按钮Radiobutton单选

4. 布局管理器

  • pack:按顺序排列(从上到下、从左到右)
  • grid:网格排列(指定行和列)
  • place:精确定位(指定坐标)

5. 事件处理

# 定义处理函数
def on_click():
    print("按钮被点击")

# 绑定事件
button = tk.Button(root, text="点击", command=on_click)

6. messagebox对话框

  • messagebox.showinfo():显示信息
  • messagebox.showwarning():显示警告
  • messagebox.showerror():显示错误

7. 美化界面

  • font:设置字体
  • fg:设置前景色(文字颜色)
  • bg:设置背景色
  • widthheight:设置大小

16.8.2 能力检查表

学习完本章后,请检查自己是否掌握了以下技能:

  • 能够创建一个tkinter窗口
  • 能够在窗口中添加标签、按钮、输入框
  • 能够使用pack和grid布局管理器
  • 能够让按钮点击时执行某个函数
  • 能够获取输入框的内容
  • 能够使用messagebox显示对话框
  • 能够设置组件的字体和颜色
  • 能够将命令行程序改写为GUI版本
  • 能够调试简单的GUI程序错误

16.8.3 常见问题快速参考

问题解决方法
窗口不显示检查是否调用了root.mainloop()
组件不显示检查是否调用了.pack().grid()
点击按钮没反应检查command=函数名是否正确(不要加括号)
输入框获取内容出错检查输入框是否为空,使用try-except处理错误
组件重叠同一个父容器中不要混用pack和grid
函数传参错误使用lambda表达式:command=lambda: func(args)

16.9 挑战练习

挑战1:计算器升级版 ⭐⭐

制作一个图形界面的计算器,要求:

  • 有数字按钮(0-9)
  • 有运算符按钮(+、-、×、÷)
  • 有等号和清除按钮
  • 有一个显示区域(Label)

提示

  • 使用grid布局排列按钮
  • 点击数字按钮时,把数字添加到显示区域
  • 点击等号时,计算表达式的结果(可以使用eval()函数)

挑战2:记账本 ⭐⭐⭐

制作一个家庭记账本,要求:

  • 可以输入收入或支出
  • 可以输入金额、类别、备注
  • 可以记录到列表中(用Label显示)
  • 可以计算总收入、总支出、结余
  • 可以清空所有记录

提示

  • 使用多个Entry输入不同信息
  • 使用Radiobutton选择收入/支出
  • 使用List或字典存储记录
  • 使用Text组件显示所有记录

挑战3:待办事项清单 ⭐⭐⭐⭐

制作一个待办事项清单(TODO List),要求:

  • 可以添加新的待办事项
  • 可以标记事项为“已完成“
  • 可以删除事项
  • 可以按优先级排序(高/中/低)
  • 可以保存到文件、从文件加载

提示

  • 使用Checkbutton标记完成状态
  • 使用Radiobutton选择优先级
  • 使用List或字典存储所有事项
  • 使用文件操作保存和加载(第13章知识)

挑战4:单词卡片学习工具 ⭐⭐⭐⭐

制作一个英语单词学习工具,要求:

  • 可以添加新单词(英文+中文)
  • 可以随机显示单词
  • 可以显示答案(中文意思)
  • 可以标记为“已掌握“或“需要复习“
  • 可以统计学习进度

提示

  • 使用字典存储单词:{"apple": "苹果", "banana": "香蕉"}
  • 使用random模块随机选择单词
  • 使用两个按钮:“显示答案“和“下一个单词”

挑战5:简单的绘图板 ⭐⭐⭐⭐⭐

制作一个简单的绘图板,要求:

  • 可以选择颜色
  • 可以选择画笔粗细
  • 可以用鼠标在Canvas(画布)上画线
  • 可以清除画布
  • 可以保存画好的图片

提示

  • 使用Canvas组件(本章未介绍,需要查资料)
  • 绑定鼠标事件:<Button-1>(点击)、<B1-Motion>(拖动)
  • 使用create_line()方法画线

16.10 下一步

恭喜你完成了第16章!你已经学会了:

✅ 创建图形界面程序 ✅ 使用常用组件(标签、按钮、输入框等) ✅ 管理界面布局 ✅ 处理用户操作(点击、输入等) ✅ 美化界面 ✅ 将命令行程序改写为GUI版本

你的技能树

Python基础
├─ 变量和数据类型 ✅
├─ 条件语句 ✅
├─ 循环语句 ✅
├─ 函数 ✅
├─ 文件操作 ✅
├─ 命令行程序 ✅
├─ 数据可视化(pyecharts)✅
└─ 图形界面程序(tkinter)✅ ← 当前位置

下一章(第17章):程序设计

在最后一章中,你将学习:

  • 如何评价一个程序的好坏
  • 变量的设计原则
  • 程序设计的一般方法
  • 如何规划一个完整的程序

这将帮助你从“会写代码“提升到“会设计程序“!


附录:tkinter速查表

A1. 常用组件速查

# 导入tkinter
import tkinter as tk
from tkinter import messagebox

# 创建主窗口
root = tk.Tk()
root.title("窗口标题")
root.geometry("400x300")

# 标签
label = tk.Label(root, text="文字")
label.pack()

# 按钮
button = tk.Button(root, text="点击", command=函数名)
button.pack()

# 输入框
entry = tk.Entry(root)
entry.pack()
content = entry.get()  # 获取内容

# 文本框(多行)
text = tk.Text(root, width=40, height=10)
text.pack()
content = text.get("1.0", "end")  # 获取内容

# 复选框
var = tk.IntVar()
checkbutton = tk.Checkbutton(root, text="选项", variable=var)
checkbutton.pack()
if var.get() == 1:  # 被选中

# 单选按钮
var = tk.StringVar()
var.set("选项1")
radio1 = tk.Radiobutton(root, text="选项1", variable=var, value="选项1")
radio2 = tk.Radiobutton(root, text="选项2", variable=var, value="选项2")
radio1.pack()
radio2.pack()

# 进入消息循环
root.mainloop()

A2. 布局管理器速查

# pack布局
component.pack()                    # 默认:从上到下
component.pack(side="left")          # 从左到右
component.pack(side="right")         # 从右到左
component.pack(padx=10, pady=10)     # 外部间距

# grid布局
component.grid(row=0, column=0)       # 第0行第0列
component.grid(row=1, column=2)       # 第1行第2列
component.grid(columnspan=2)         # 跨2列
component.grid(rowspan=3)            # 跨3行

# place布局
component.place(x=100, y=50)         # 精确定位

A3. 颜色速查

# 前景色(文字颜色)
fg="red"
fg="#FF0000"

# 背景色
bg="blue"
bg="#0000FF"

# 常用颜色
# red, blue, green, yellow, black, white, orange, purple

A4. messagebox速查

from tkinter import messagebox

# 信息对话框
messagebox.showinfo("标题", "消息内容")

# 警告对话框
messagebox.showwarning("标题", "消息内容")

# 错误对话框
messagebox.showerror("标题", "消息内容")

# 确认对话框(返回"yes"或"no")
result = messagebox.askyesno("标题", "确定吗?")

# 是/否/取消对话框(返回"yes"、"no"或"cancel")
result = messagebox.askyesnocancel("标题", "确定吗?")

学习建议

  1. 多动手实践,每个例子都亲自运行一遍
  2. 尝试修改代码,看看效果有什么变化
  3. 把之前的CLI程序改写成GUI版本
  4. 设计自己的小工具(如记账本、学习计划表)
  5. 遇到问题时,先用print或messagebox调试

记住:学习编程最好的方法就是多写代码!💪

第17章 程序设计 - 如何写出好程序

引言:从“能运行“到“写得好“

想象一下,有两个同学都写了一个“猜数字“游戏程序:

同学A的程序

import random
n=random.randint(1,100)
while True:
    g=int(input("?"))
    if g>n:print("大")
    elif g<n:print("小")
    else:print("对");break

同学B的程序

import random

# 猜数字游戏
# 目标:猜一个1-100之间的随机数

secret_number = random.randint(1, 100)
guess_count = 0

print("=== 猜数字游戏 ===")
print("我已经想好了一个1到100之间的数字")
print("你能猜到它是多少吗?")

while True:
    guess = input("请输入你的猜测(输入q退出):")

    # 允许用户退出
    if guess == 'q':
        print(f"游戏结束!正确答案是:{secret_number}")
        break

    # 验证输入
    if not guess.isdigit():
        print("请输入一个有效的数字!")
        continue

    guess_count += 1
    guess = int(guess)

    # 比较大小
    if guess > secret_number:
        print(f"{guess} 太大了!再试试")
    elif guess < secret_number:
        print(f"{guess} 太小了!再试试")
    else:
        print(f"恭喜你!{guess} 就是正确答案!")
        print(f"你总共猜了 {guess_count} 次")
        break

两个程序的对比

对比项同学A的程序同学B的程序
能否运行✅ 能运行✅ 能运行
变量命名❌ n, g(看不懂)✅ secret_number, guess(清晰)
注释❌ 没有注释✅ 有注释说明
用户体验❌ 提示不清楚✅ 友好的提示
错误处理❌ 输入非数字会崩溃✅ 验证输入,可以退出
代码格式❌ 一行多语句✅ 格式清晰

问题:如果你是老师,你会给哪个程序更高的分数?

答案:当然是同学B!虽然两个程序都能“运行“,但同学B的程序:

  • 更容易理解和修改
  • 更健壮(不容易出错)
  • 用户体验更好

什么是“好程序“?

好程序不仅要能运行,还要:

  1. 正确性 - 能正确解决问题
  2. 可读性 - 别人能看懂你的代码
  3. 可维护性 - 容易修改和扩展
  4. 健壮性 - 不容易出错
  5. 效率 - 运行速度快,占用资源少

为什么要学习“程序设计“?

你可能在想:“我写的程序能运行不就行了吗?为什么要学这些?”

答案是

  1. 让代码更容易理解

    • 过了一个月,你自己都看不懂自己写的代码
    • 别人看你的代码像看“天书“
  2. 让代码更容易修改

    • 需求变化时,不用重写整个程序
    • 可以轻松添加新功能
  3. 减少程序错误

    • 好的设计让错误更少
    • 出错了也更容易找到
  4. 为大型程序做准备

    • 写10行的程序,随便写写也行
    • 写100行、1000行的程序,就需要好的设计方法
  5. 成为一名优秀的程序员

    • 初级程序员:能写出能运行的代码
    • 高级程序员:能写出优雅、健壮的代码

本章学习路线

第1步:如何评价一个程序的好坏
   ↓
第2步:变量设计的原则
   ↓
第3步:程序设计的一般方法
   ↓
第4步:综合实践:用设计方法重写程序
   ↓
第5步:常见错误和调试技巧

给家长的小贴士:这一章是理论性较强的章节,但非常重要。建议家长:

  • 用“写作文“类比程序设计:作文不仅要通顺,还要有结构、有逻辑
  • 鼓励孩子对比自己之前的程序,找出可以改进的地方
  • 强调“好习惯“的重要性:现在养成好习惯,以后写程序会更轻松
  • 可以让孩子“代码审查“家长写的程序,找出问题

17.1 如何评价一个程序的好坏

程序评价的五个维度

我们可以从以下五个维度评价一个程序:

1. 正确性(Correctness)

定义:程序是否正确地解决了问题。

如何判断

  • 输入合法数据,输出结果是否正确?
  • 边界情况是否正确?(例如:空列表、0、负数)
  • 错误输入是否妥善处理?

举例

写一个“计算平均分“的程序:

不正确的版本

scores = [80, 90, 85]
average = sum(scores) / 3  # 假设固定3个科目
print(average)

问题:如果列表不是3个科目,结果就错了!

正确的版本

def calculate_average(scores):
    if not scores:  # 处理空列表
        return 0
    return sum(scores) / len(scores)

# 测试各种情况
print(calculate_average([80, 90, 85]))      # 正常情况
print(calculate_average([]))                 # 空列表
print(calculate_average([100]))               # 一个元素

给家长的小贴士:教导孩子“测试思维“

  • 编写程序后,要主动测试各种情况
  • 询问:“如果输入是空的会怎样?” “如果是负数会怎样?”
  • 这能培养孩子的严谨思维

2. 可读性(Readability)

定义:程序是否容易被人类理解。

评价标准

  • ✅ 变量名能说明用途
  • ✅ 有适当的注释
  • ✅ 代码格式整齐
  • ✅ 逻辑清晰

举例对比

可读性差的代码

a=10
b=20
c=a*b
print(c)

问题:a、b、c是什么意思?

可读性好的代码

length = 10      # 长方形的长
width = 20       # 长方形的宽
area = length * width  # 计算面积
print(f"面积是:{area}")

可读性的三个黄金法则

  1. 好命名

    # ❌ 差的命名
    x = 10
    s = "hello"
    flg = True
    
    # ✅ 好的命名
    age = 10
    user_name = "hello"
    is_valid = True
    
  2. 好注释

    # ❌ 没有注释
    s = s.replace(" ", "")
    
    # ✅ 有注释
    # 去除用户名中的所有空格
    user_name = user_name.replace(" ", "")
    
  3. 好格式

    # ❌ 格式混乱
    if x>0:x=x+1;print(x)
    
    # ✅ 格式清晰
    if x > 0:
        x = x + 1
        print(x)
    

给家长的小贴士:鼓励孩子“像讲故事一样写代码“

  • 变量名要像一个“好标题“
  • 注释要像“故事说明“
  • 代码要像“分段清晰的文章“

3. 可维护性(Maintainability)

定义:程序是否容易被修改和扩展。

问题场景

  • 需求变化时,是否需要大改代码?
  • 修复一个bug,会不会引入新的bug?
  • 能否轻松添加新功能?

举例

可维护性差的代码

# 硬编码的值,难以修改
price = 100
tax = price * 0.13  # 税率13%写死在代码里
total = price + tax
print(total)

问题:如果税率改成15%,要修改所有地方!

可维护性好的代码

# 使用常量,易于修改
TAX_RATE = 0.13  # 税率集中定义

def calculate_total(price):
    """计算含税总价"""
    tax = price * TAX_RATE
    return price + tax

print(calculate_total(100))
print(calculate_total(200))

优势:修改税率只需要改一行!

可维护性的技巧

  1. 避免重复代码(DRY原则)

    # ❌ 重复代码
    area1 = length1 * width1
    area2 = length2 * width2
    area3 = length3 * width3
    
    # ✅ 使用函数
    def calculate_area(length, width):
        return length * width
    
    area1 = calculate_area(length1, width1)
    area2 = calculate_area(length2, width2)
    area3 = calculate_area(length3, width3)
    
  2. 使用函数封装

    # ❌ 所有代码堆在一起
    a = 10
    b = 20
    print(a + b)
    c = 30
    d = 40
    print(c + d)
    
    # ✅ 封装成函数
    def add_and_print(x, y):
        print(x + y)
    
    add_and_print(10, 20)
    add_and_print(30, 40)
    

给家长的小贴士

  • 用“乐高积木“比喻函数:每块积木(函数)做好后,可以重复使用
  • 当孩子写类似的代码时,提醒:“这能不能做成一个函数?”

4. 健壮性(Robustness)

定义:程序是否能处理各种异常情况,不容易崩溃。

问题场景

  • 用户输入错误时,程序是否崩溃?
  • 数据不完整时,程序是否继续运行?
  • 出现错误时,是否有友好的提示?

举例

不健壮的代码

age = int(input("请输入年龄:"))
print(f"你明年{age + 1}岁")

问题:如果用户输入“abc“,程序崩溃!

健壮的代码

while True:
    age_input = input("请输入年龄:")

    # 验证输入是否为数字
    if not age_input.isdigit():
        print("请输入一个有效的数字!")
        continue

    age = int(age_input)

    # 验证年龄范围
    if age < 0 or age > 150:
        print("年龄应该在0-150之间!")
        continue

    print(f"你明年{age + 1}岁")
    break

健壮性的三个原则

  1. 永远不要相信用户输入

    # 假设用户可能输入任何东西
    user_input = input("请输入数字:")
    
    # 先验证再使用
    if user_input.isdigit():
        number = int(user_input)
        # 使用这个数字
    else:
        print("输入无效!")
    
  2. 处理边界情况

    # 边界情况:空列表
    scores = []
    if scores:  # 先检查是否为空
        average = sum(scores) / len(scores)
    else:
        print("没有成绩数据")
    
  3. 提供有用的错误信息

    # ❌ 无用的错误信息
    print("错误")
    
    # ✅ 有用的错误信息
    print("错误:文件不存在,请检查文件名是否正确")
    

给家长的小贴士

  • 和孩子玩“找茬游戏“:让孩子想方设法“破坏“程序
  • 问孩子:“如果你故意输入错误,程序会怎样?”
  • 这能培养孩子的“防御性编程“思维

5. 效率(Efficiency)

定义:程序运行的速度和占用的资源。

对于初学者

  • 不要过度优化
  • “先让它能运行,再让它运行得更快”
  • 可读性比效率更重要

什么时候需要考虑效率

  • 数据量很大时(例如:处理10000条数据)
  • 程序运行很慢时
  • 使用有限的资源(例如:内存)

简单优化技巧

  1. 避免重复计算

    # ❌ 重复计算
    for i in range(1000):
        result = sum(very_large_list) * i  # 每次循环都重新计算
    
    # ✅ 只计算一次
    total = sum(very_large_list)
    for i in range(1000):
        result = total * i
    
  2. 选择合适的数据结构

    # 如果需要频繁查找,用字典而不是列表
    
    # ❌ 用列表查找(慢)
    students_list = ["Alice", "Bob", "Charlie"]
    if "Bob" in students_list:  # 需要遍历整个列表
        print("找到Bob")
    
    # ✅ 用字典查找(快)
    students_dict = {"Alice": 1, "Bob": 2, "Charlie": 3}
    if "Bob" in students_dict:  # 直接查找
        print("找到Bob")
    

给家长的小贴士

  • 告诉孩子:“就像写作文,先写完,再润色”
  • 对于初学者,可读性 > 效率
  • 如果程序运行速度能接受,就不需要优化

综合评价案例

让我们用五个维度评价一个完整的程序:

题目:写一个“学生成绩管理“程序

版本A:基础版

scores = []
while True:
    s = input("输入成绩(q退出):")
    if s == 'q':
        break
    scores.append(int(s))
print(sum(scores)/len(scores))

版本B:改进版

def calculate_average(scores):
    """计算平均分"""
    if not scores:
        return 0
    return sum(scores) / len(scores)

def get_valid_score(prompt):
    """获取有效的成绩输入"""
    while True:
        user_input = input(prompt)

        if user_input.lower() == 'q':
            return None

        # 验证输入
        if not user_input.isdigit():
            print("请输入一个有效的数字!")
            continue

        score = int(user_input)

        # 验证范围
        if score < 0 or score > 100:
            print("成绩应该在0-100之间!")
            continue

        return score

def main():
    """主程序"""
    print("=== 学生成绩管理系统 ===")

    scores = []
    while True:
        score = get_valid_score("请输入成绩(0-100,输入q退出):")

        if score is None:
            break

        scores.append(score)

    if scores:
        average = calculate_average(scores)
        highest = max(scores)
        lowest = min(scores)

        print(f"\n=== 成绩统计 ===")
        print(f"学生人数:{len(scores)}")
        print(f"平均分:{average:.2f}")
        print(f"最高分:{highest}")
        print(f"最低分:{lowest}")
    else:
        print("没有输入成绩数据")

if __name__ == "__main__":
    main()

评价对比

维度版本A版本B
正确性✅ 基本正确✅ 完全正确(处理空列表)
可读性❌ 变量名s不明确✅ 变量名清晰,有注释
可维护性❌ 代码重复,难扩展✅ 使用函数,易扩展
健壮性❌ 输入非数字会崩溃✅ 验证输入和范围
效率✅ 效率可以接受✅ 效率可以接受

结论:版本B是一个更好的程序!

练习1:评价以下程序

请用五个维度评价以下程序,指出优点和缺点:

# 计算圆的面积
r = float(input("请输入半径:"))
area = 3.14 * r * r
print(area)
📝 查看评价

评价结果

  1. 正确性:⭐⭐⭐⭐

    • ✅ 计算公式正确
    • ⚠️ π只用3.14,精度不够
  2. 可读性:⭐⭐⭐

    • ✅ 变量名r和area还算清晰
    • ❌ 没有注释
    • ❌ 输出没有说明
  3. 可维护性:⭐⭐⭐

    • ⚠️ π的值硬编码
    • ✅ 逻辑简单,容易理解
  4. 健壮性:⭐

    • ❌ 没有检查半径是否为负数
    • ❌ 输入非数字会崩溃
  5. 效率:⭐⭐⭐⭐⭐

    • ✅ 简单计算,效率很高

改进建议

import math

def calculate_circle_area(radius):
    """计算圆的面积"""
    if radius <= 0:
        raise ValueError("半径必须大于0")
    return math.pi * radius * radius

try:
    radius = float(input("请输入圆的半径:"))
    area = calculate_circle_area(radius)
    print(f"半径为 {radius} 的圆的面积是:{area:.2f}")
except ValueError as e:
    print(f"输入错误:{e}")

17.2 变量的设计

为什么变量设计很重要?

变量是程序中最基本的“存储单元“。变量设计得好坏,直接影响程序的质量。

比喻

  • 变量就像收纳箱
  • 好的变量设计:每个箱子贴上清晰的标签,只放一种物品
  • 坏的变量设计:箱子没有标签,什么都混在一起

变量设计的三个原则

原则1:职责明确(Single Responsibility)

核心思想:一个变量只承担一个职责。

不好的例子

# x既用来存储输入,又用来存储计算结果
x = input("请输入数字:")
x = int(x) * 2
print(x)

问题:x的职责不清晰,容易混淆。

好的例子

# 每个变量职责明确
user_input = input("请输入数字:")
number = int(user_input)
doubled_number = number * 2
print(doubled_number)

比喻

  • 像整理房间,一个抽屉只放一种东西
  • “袜子抽屉“只放袜子,“内衣抽屉“只放内衣

原则2:命名规范(Meaningful Names)

核心思想:变量名要能“自解释“,让人一看就知道它存的是什么。

命名规范

  1. 使用有意义的英文名词

    # ❌ 不好的命名
    a = 10
    x = "hello"
    f1 = 3.14
    
    # ✅ 好的命名
    age = 10
    user_name = "hello"
    pi = 3.14
    
  2. 使用完整单词,不要过度缩写

    # ❌ 过度缩写
    s = "hello"  # s是什么?
    nm = "Alice"  # nm是name吗?
    cnt = 0       # cnt是count吗?
    
    # ✅ 完整单词
    name = "hello"
    username = "Alice"
    count = 0
    
  3. 使用下划线分隔多个单词(snake_case)

    # ❌ 不符合Python风格
    userName = "Alice"
    StudentAge = 10
    
    # ✅ Python风格(snake_case)
    user_name = "Alice"
    student_age = 10
    
  4. 布尔变量用is/has/can开头

    # ✅ 清晰的布尔命名
    is_valid = True
    has_permission = False
    can_edit = True
    is_empty = False
    
    # ❌ 不清晰的布尔命名
    valid = True
    permission = False
    edit = True
    
  5. 集合类型用复数名词

    # ✅ 清晰的集合命名
    students = ["Alice", "Bob", "Charlie"]
    scores = [80, 90, 85]
    books = ["书1", "书2", "书3"]
    
    # ❌ 不清晰的集合命名
    student_list = ["Alice", "Bob", "Charlie"]
    score_array = [80, 90, 85]
    

命名禁忌

  • ❌ 使用单个字母(除了循环变量i, j, k)
  • ❌ 使用拼音(如xingming)
  • ❌ 使用中文(Python3支持但不推荐)
  • ❌ 使用关键字(如if, for, while)
  • ❌ 使用特殊字符(如@, #, $)

给家长的小贴士

  • 像教孩子写作文“起标题“一样,教孩子给变量起名
  • 鼓励孩子使用“能看懂的英文“,而不是“a“, “b”, “c”
  • 可以建议孩子准备一个“变量命名词典“,记录常用的英文单词

原则3:作用域最小化(Minimize Scope)

核心思想:变量的作用范围越小越好。

什么是作用域

  • 变量能被访问的代码范围
  • 在函数内部定义的变量,只能在函数内部使用
  • 在函数外部定义的变量,可以在整个程序中使用

为什么作用域要小

  • 减少变量被意外修改的风险
  • 让程序更容易理解
  • 降低代码的耦合度

不好的例子

# 全局变量,任何地方都能修改
counter = 0

def increment():
    global counter
    counter = counter + 1

def decrement():
    global counter
    counter = counter - 1

increment()
decrement()
print(counter)

问题:counter是全局变量,任何函数都能修改,容易出错。

好的例子

def process_numbers(numbers):
    """局部变量,作用域在函数内"""
    counter = 0
    total = 0

    for num in numbers:
        counter += 1
        total += num

    average = total / counter
    return average

result = process_numbers([10, 20, 30])
print(result)

作用域最小化的技巧

  1. 优先使用局部变量

    # ✅ 好的做法:变量定义在需要的地方
    def calculate_rectangle_area(length, width):
        area = length * width  # 局部变量
        return area
    
  2. 避免全局变量

    # ❌ 不好:使用全局变量
    TAX_RATE = 0.13
    
    def calculate_tax(price):
        return price * TAX_RATE
    
    # ✅ 更好:作为参数传递
    def calculate_tax(price, tax_rate):
        return price * tax_rate
    
  3. 循环变量的作用域

    # ✅ 好的做法:循环变量只在循环中使用
    for i in range(10):
        print(i)
    # i在循环外就没有意义了
    

给家长的小贴士

  • 用“隐私“的概念比喻作用域
  • “就像你的日记本,只有你自己能看,别人不能随便翻”
  • 变量也是一样,尽量让它“私有“,不要让所有人都能访问

变量设计的实战案例

案例:设计一个“学生成绩统计“程序

需求

  • 输入多个学生的成绩
  • 计算平均分、最高分、最低分
  • 统计及格人数

设计1:不好的变量设计

# ❌ 变量命名不清晰
a = []
while True:
    b = input("输入成绩:")
    if b == 'q':
        break
    a.append(int(b))

c = sum(a) / len(a)
d = max(a)
e = min(a)
f = 0
for g in a:
    if g >= 60:
        f = f + 1

print(c, d, e, f)

问题

  • a, b, c, d, e, f, g这些变量名完全没有意义
  • 看代码完全不知道在做什么

设计2:改进的变量设计

# ✅ 变量命名清晰,职责明确
scores = []           # 存储所有成绩
passing_score = 60    # 及格分数线

while True:
    user_input = input("请输入成绩(输入q退出):")
    if user_input == 'q':
        break
    score = int(user_input)
    scores.append(score)

if scores:
    average_score = sum(scores) / len(scores)  # 平均分
    highest_score = max(scores)                 # 最高分
    lowest_score = min(scores)                  # 最低分

    passing_count = 0  # 及格人数
    for score in scores:
        if score >= passing_score:
            passing_count += 1

    print(f"平均分:{average_score:.2f}")
    print(f"最高分:{highest_score}")
    print(f"最低分:{lowest_score}")
    print(f"及格人数:{passing_count}/{len(scores)}")

优点

  • 每个变量的职责都很明确
  • 变量名能“自解释“
  • 代码易于理解和维护

练习2:改进变量设计

请改进以下程序的变量设计:

x = input("请输入学生姓名:")
y = int(input("请输入语文成绩:"))
z = int(input("请输入数学成绩:"))
w = int(input("请输入英语成绩:"))
v = y + z + w
u = v / 3
print(f"{x}的总分是{v},平均分是{u}")
📝 查看改进方案
# ✅ 改进后的版本
student_name = input("请输入学生姓名:")
chinese_score = int(input("请输入语文成绩:"))
math_score = int(input("请输入数学成绩:"))
english_score = int(input("请输入英语成绩:"))

total_score = chinese_score + math_score + english_score
average_score = total_score / 3

print(f"{student_name}的总分是{total_score},平均分是{average_score:.2f}")

改进点

  • x → student_name:明确表示学生姓名
  • y, z, w → chinese_score, math_score, english_score:分别表示各科成绩
  • v → total_score:总分
  • u → average_score:平均分
  • 添加:.2f格式化平均分输出

17.3 程序设计的一般方法

从“想到哪里写到哪里“到“系统化设计“

初学者的做法

看到题目 → 直接开始写代码 → 边写边想 → 不断修改

程序员的做法

分析需求 → 设计方案 → 编写代码 → 测试调试 → 优化改进

比喻

  • 初学者:像没有地图就开车,开到哪算哪
  • 程序员:先规划路线,再出发

程序设计的五个步骤

第1步:明确需求(Understand Requirements)

核心问题

  • 程序要解决什么问题?
  • 输入是什么?输出是什么?
  • 有哪些特殊要求?

方法

  1. 列出输入和输出

    题目:写一个猜数字游戏
    
    输入:
    - 用户猜测的数字
    
    输出:
    - "太大了"、"太小了"、"正确"的提示
    - 猜测次数统计
    
  2. 明确功能要求

    功能要求:
    1. 生成1-100之间的随机数
    2. 用户可以多次猜测
    3. 每次猜测后给出提示
    4. 猜中后显示猜测次数
    5. 允许用户中途退出
    
  3. 确定边界条件

    边界情况:
    - 用户输入的不是数字怎么办?
    - 用户输入的数字超出范围怎么办?
    - 用户一直猜不中怎么办?
    

给家长的小贴士

  • 让孩子把需求“说出来“或“写下来“
  • 问孩子:“这个程序要做什么?输入是什么?输出是什么?”
  • 这能培养孩子的“需求分析“能力

第2步:设计输入输出(Design Input/Output)

核心问题

  • 用户如何输入数据?
  • 程序如何展示结果?

方法

  1. 设计输入格式

    # 猜数字游戏的输入设计
    
    方案1:简单输入
    请输入你的猜测:50
    
    方案2:带提示的输入
    猜数字游戏(1-100)
    第1次猜测,请输入数字:50
    (输入q退出)
    
    选择方案2,更友好!
    
  2. 设计输出格式

    # 猜数字游戏的输出设计
    
    方案1:简单输出
    太大了
    太小了
    正确
    
    方案2:详细输出
    ===== 猜数字游戏 =====
    第1次猜测:50
    提示:太大了!
    你已经猜了1次
    
    选择方案2,用户体验更好!
    

输入输出设计的原则

  • ✅ 提示信息清晰
  • ✅ 输入格式简单
  • ✅ 输出结果直观
  • ✅ 提供退出选项

第3步:设计主要步骤(Design Main Steps)

核心问题

  • 程序的主要步骤是什么?
  • 用什么控制结构?

方法:使用伪代码(Pseudocode)

什么是伪代码

  • 介于自然语言和编程语言之间的描述
  • 不需要语法正确
  • 重点是把逻辑说清楚

举例:猜数字游戏的伪代码

# 伪代码:猜数字游戏

1. 生成一个1-100的随机数
2. 初始化猜测次数为0
3. 循环:
   3.1 提示用户输入猜测
   3.2 如果用户输入q,退出循环
   3.3 验证输入是否为有效数字
   3.4 猜测次数加1
   3.5 比较猜测和目标数字
       如果猜中:显示恭喜,退出循环
       如果太大:提示"太大"
       如果太小:提示"太小"
4. 显示游戏结果

流程图表示

    开始
      ↓
生成随机数
      ↓
  初始化次数
      ↓
  ┌─────────┐
  │  循环   │←──────┐
  └─────────┘       │
      ↓             │
  获取用户输入      │
      ↓             │
  输入q? → 结束     │
      ↓ 否          │
  验证输入          │
      ↓             │
  次数+1            │
      ↓             │
  比较大小          │
      ↓             │
  猜中? → 是 → 结束  │
      ↓ 否          │
  提示大小 ─────────┘

第4步:设计数据结构(Design Data Structures)

核心问题

  • 用什么变量存储数据?
  • 用什么数据结构(列表、字典等)?

方法

  1. 列出需要的变量

    # 猜数字游戏需要的变量
    
    secret_number     # 目标数字(整数)
    guess_count       # 猜测次数(整数)
    user_input        # 用户输入(字符串)
    guess             # 猜测的数字(整数)
    is_correct        # 是否猜中(布尔值)
    
  2. 确定数据结构

    # 简单情况:用基本变量
    secret_number = 50
    guess_count = 0
    
    # 复杂情况:用列表或字典
    # 如果要记录所有猜测历史
    guess_history = [50, 25, 37, 42]
    
    # 如果要记录多个玩家的成绩
    player_scores = {
        "小明": 5,
        "小红": 7
    }
    

数据结构选择原则

  • 数据量小 → 用基本变量
  • 数据有顺序 → 用列表
  • 数据需要查找 → 用字典

第5步:设计验证方法(Design Verification)

核心问题

  • 如何验证程序是否正确?
  • 需要测试哪些情况?

方法

  1. 设计测试用例

    # 猜数字游戏的测试用例
    
    测试用例1:正常情况
    - 目标数字:50
    - 猜测:30, 60, 40, 55, 45, 50
    - 预期输出:太小、太大、太小、太大、太小、正确
    
    测试用例2:边界情况
    - 目标数字:1
    - 猜测:0, 2, 1
    - 预期输出:无效、太大、正确
    
    测试用例3:错误输入
    - 目标数字:50
    - 猜测:abc, 50
    - 预期输出:提示输入有效数字、正确
    
    测试用例4:中途退出
    - 目标数字:50
    - 猜测:q
    - 预期输出:显示游戏结束和答案
    
  2. 验证方法

    # 方法1:手工验证
    print("目标数字是:50")  # 调试输出
    # 手动输入各种测试数据
    
    # 方法2:自动测试
    def test_guess_number():
        # 模拟测试
        assert compare_guess(50, 30) == "太小"
        assert compare_guess(50, 60) == "太大"
        assert compare_guess(50, 50) == "正确"
        print("所有测试通过!")
    

综合案例:完整的设计过程

题目:设计一个“成绩管理系统“

第1步:明确需求

功能需求:
1. 添加学生成绩
2. 查询学生成绩
3. 计算班级平均分
4. 找出最高分和最低分
5. 显示所有成绩

输入:
- 命令选择(1-5)
- 学生姓名
- 成绩分数

输出:
- 操作结果
- 成绩列表
- 统计信息

第2步:设计输入输出

# 输入设计
========== 成绩管理系统 ==========
1. 添加成绩
2. 查询成绩
3. 统计信息
4. 显示所有成绩
5. 退出
请选择操作(1-5):_

# 输出设计
✓ 成绩添加成功!
✓ 查询结果:小明的成绩是85分

=== 班级统计 ===
学生人数:30
平均分:82.5
最高分:98(小红)
最低分:45(小刚)

第3步:设计主要步骤(伪代码)

# 伪代码

main:
    初始化成绩字典
    循环:
        显示菜单
        获取用户选择
        根据选择执行操作:
            1 → add_score()
            2 → query_score()
            3 → show_statistics()
            4 → show_all_scores()
            5 → 退出程序
        如果选择无效,提示错误

add_score:
    输入学生姓名
    输入成绩分数
    验证成绩范围(0-100)
    添加到字典
    显示成功消息

query_score:
    输入学生姓名
    在字典中查找
    如果找到,显示成绩
    如果没找到,提示不存在

show_statistics:
    如果字典为空,提示无数据
    否则:
        计算平均分
        找最高分
        找最低分
        显示统计结果

第4步:设计数据结构

# 数据结构设计

# 主数据结构:用字典存储成绩
# key: 学生姓名(字符串)
# value: 成绩分数(整数)
scores = {
    "小明": 85,
    "小红": 92,
    "小刚": 78
}

# 辅助变量
command = ""        # 用户命令
student_name = ""   # 学生姓名
score = 0          # 成绩分数

第5步:设计验证方法

# 测试用例

测试1:添加成绩
输入:小明,85
预期:添加成功,scores中有{"小明": 85}

测试2:查询成绩
输入:小明
预期:显示"小明的成绩是85分"

测试3:无效成绩
输入:小刚,150
预期:提示"成绩应该在0-100之间"

测试4:查询不存在的学生
输入:小华
预期:提示"找不到该学生"

测试5:统计信息
输入:scores = {"小明":85, "小红":92, "小刚":78}
预期:平均分=85,最高=92,最低=78

完整代码实现

def display_menu():
    """显示菜单"""
    print("\n========== 成绩管理系统 ==========")
    print("1. 添加成绩")
    print("2. 查询成绩")
    print("3. 统计信息")
    print("4. 显示所有成绩")
    print("5. 退出")
    print("=" * 30)

def add_score(scores):
    """添加成绩"""
    name = input("请输入学生姓名:")
    score_str = input("请输入成绩(0-100):")

    # 验证输入
    if not score_str.isdigit():
        print("错误:请输入有效的数字!")
        return

    score = int(score_str)

    # 验证范围
    if score < 0 or score > 100:
        print("错误:成绩应该在0-100之间!")
        return

    # 添加成绩
    scores[name] = score
    print(f"✓ 成功添加{name}的成绩:{score}分")

def query_score(scores):
    """查询成绩"""
    name = input("请输入要查询的学生姓名:")

    if name in scores:
        print(f"✓ {name}的成绩是{scores[name]}分")
    else:
        print(f"✗ 找不到学生:{name}")

def show_statistics(scores):
    """显示统计信息"""
    if not scores:
        print("✗ 还没有成绩数据!")
        return

    score_list = list(scores.values())
    average = sum(score_list) / len(score_list)
    highest = max(score_list)
    lowest = min(score_list)

    # 找出最高分和最低分的学生
    highest_students = [name for name, score in scores.items() if score == highest]
    lowest_students = [name for name, score in scores.items() if score == lowest]

    print("\n=== 班级统计 ===")
    print(f"学生人数:{len(scores)}")
    print(f"平均分:{average:.2f}")
    print(f"最高分:{highest}分({', '.join(highest_students)})")
    print(f"最低分:{lowest}分({', '.join(lowest_students)})")

def show_all_scores(scores):
    """显示所有成绩"""
    if not scores:
        print("✗ 还没有成绩数据!")
        return

    print("\n=== 所有成绩 ===")
    for name, score in scores.items():
        print(f"{name}:{score}分")

def main():
    """主程序"""
    scores = {}  # 存储成绩的字典

    while True:
        display_menu()
        command = input("请选择操作(1-5):")

        if command == "1":
            add_score(scores)
        elif command == "2":
            query_score(scores)
        elif command == "3":
            show_statistics(scores)
        elif command == "4":
            show_all_scores(scores)
        elif command == "5":
            print("感谢使用成绩管理系统!")
            break
        else:
            print("✗ 无效的选择,请输入1-5之间的数字!")

if __name__ == "__main__":
    main()

练习3:设计程序

按照上述五个步骤,设计一个“图书馆借阅系统“。

需求

  • 添加图书(书名、作者)
  • 借阅图书(记录借阅人)
  • 归还图书
  • 查询图书状态
  • 显示所有图书

请写出:

  1. 需求分析
  2. 输入输出设计
  3. 主要步骤(伪代码)
  4. 数据结构设计
  5. 测试用例
📝 查看设计参考
# 1. 需求分析

功能需求:
- 添加图书:输入书名和作者,添加到图书馆
- 借阅图书:输入书名和借阅人,记录借阅
- 归还图书:输入书名,清除借阅信息
- 查询图书:输入书名,显示图书状态(在馆/借出)
- 显示所有图书:列出所有图书及状态

# 2. 输入输出设计

========== 图书馆管理系统 ==========
1. 添加图书
2. 借阅图书
3. 归还图书
4. 查询图书
5. 显示所有图书
6. 退出
请选择操作(1-6):_

# 3. 主要步骤(伪代码)

main:
    初始化图书字典
    循环:
        显示菜单
        获取用户选择
        根据选择执行操作
        如果选择6,退出程序

add_book:
    输入书名
    输入作者
    添加到字典
    标记为"在馆"

borrow_book:
    输入书名
    输入借阅人
    如果图书在馆:
        标记为"借出"
        记录借阅人
    否则:
        提示图书已借出

return_book:
    输入书名
    如果图书已借出:
        标记为"在馆"
        清除借阅人
    否则:
        提示图书在馆

query_book:
    输入书名
    如果图书存在:
        显示书名、作者、状态
        如果已借出,显示借阅人
    否则:
        提示图书不存在

# 4. 数据结构设计

books = {
    "书名": {
        "author": "作者名",
        "borrower": "借阅人",  # None表示在馆
        "is_borrowed": False   # False表示在馆
    }
}

# 5. 测试用例

测试1:添加图书
输入:Python编程,小明
预期:成功添加,状态为"在馆"

测试2:借阅图书
输入:Python编程,小红
预期:成功借阅,借阅人为"小红"

测试3:重复借阅
输入:Python编程,小刚
预期:提示"图书已被小红借出"

测试4:归还图书
输入:Python编程
预期:成功归还,状态为"在馆"

测试5:查询不存在的图书
输入:Java编程
预期:提示"图书不存在"

17.4 综合实践:重写之前的程序

现在我们已经学习了程序设计的理论,让我们用这些知识来改进之前写过的程序!

案例1:改进“猜数字游戏“

回顾:你在第8章学过猜数字游戏

原始版本(可能的样子):

import random

n = random.randint(1, 100)
c = 0

while True:
    g = input("请输入数字:")
    if g == 'q':
        break

    g = int(g)
    c = c + 1

    if g > n:
        print("太大了")
    elif g < n:
        print("太小")
    else:
        print("对了")
        break

print(c)

问题分析

  • ❌ 变量命名不清晰(n, c, g)
  • ❌ 没有输入验证
  • ❌ 没有注释
  • ❌ 用户提示不友好
  • ❌ 输出格式简陋

改进版本

import random

def guess_number_game():
    """猜数字游戏 - 猜一个1到100之间的随机数"""

    print("=== 猜数字游戏 ===")
    print("我已经想好了一个1到100之间的数字")
    print("你能猜到它是多少吗?")
    print("(输入q可以随时退出)\n")

    # 生成随机数
    secret_number = random.randint(1, 100)
    guess_count = 0
    guess_history = []  # 记录猜测历史

    while True:
        # 获取用户输入
        user_input = input(f"第{guess_count + 1}次猜测,请输入数字:")

        # 检查是否退出
        if user_input.lower() == 'q':
            print(f"\n游戏结束!正确答案是:{secret_number}")
            return

        # 验证输入
        if not user_input.isdigit():
            print("请输入一个有效的数字!\n")
            continue

        guess = int(user_input)

        # 验证范围
        if guess < 1 or guess > 100:
            print("请输入1到100之间的数字!\n")
            continue

        # 记录猜测
        guess_count += 1
        guess_history.append(guess)

        # 比较大小
        if guess > secret_number:
            print(f"{guess} 太大了!")
        elif guess < secret_number:
            print(f"{guess} 太小了!")
        else:
            print(f"\n🎉 恭喜你!{guess} 就是正确答案!")
            print(f"你总共猜了 {guess_count} 次")

            # 显示猜测历史
            if guess_count > 1:
                print(f"猜测历史:{guess_history}")
            break

        # 提供提示
        print()

if __name__ == "__main__":
    guess_number_game()

改进点

  • ✅ 清晰的函数名和变量名
  • ✅ 完整的文档字符串
  • ✅ 输入验证(数字、范围)
  • ✅ 友好的用户界面
  • ✅ 猜测历史记录
  • ✅ 退出功能
  • ✅ 格式化输出

案例2:改进“计算器程序“

原始版本(可能的样子):

n1 = int(input("第1个数:"))
n2 = int(input("第2个数:"))
op = input("运算符:")

if op == "+":
    print(n1 + n2)
elif op == "-":
    print(n1 - n2)
elif op == "*":
    print(n1 * n2)
elif op == "/":
    print(n1 / n2)

问题分析

  • ❌ 没有除零检查
  • ❌ 没有输入验证
  • ❌ 没有循环功能
  • ❌ 输出简陋

改进版本

def calculator():
    """简单计算器 - 支持加减乘除运算"""

    print("=== 简单计算器 ===")
    print("支持的运算:+ - * /")
    print("输入q退出\n")

    while True:
        try:
            # 获取第一个数字
            num1_str = input("请输入第一个数字:")
            if num1_str.lower() == 'q':
                break

            num1 = float(num1_str)

            # 获取运算符
            operator = input("请输入运算符(+ - * /):")
            if operator not in ['+', '-', '*', '/']:
                print("无效的运算符!请输入 + - * /\n")
                continue

            # 获取第二个数字
            num2_str = input("请输入第二个数字:")
            num2 = float(num2_str)

            # 计算结果
            result = None
            if operator == '+':
                result = num1 + num2
            elif operator == '-':
                result = num1 - num2
            elif operator == '*':
                result = num1 * num2
            elif operator == '/':
                # 除零检查
                if num2 == 0:
                    print("错误:不能除以零!\n")
                    continue
                result = num1 / num2

            # 显示结果
            print(f"结果:{num1} {operator} {num2} = {result}\n")

        except ValueError:
            print("错误:请输入有效的数字!\n")

if __name__ == "__main__":
    calculator()

改进点

  • ✅ 支持小数计算(使用float)
  • ✅ 除零检查
  • ✅ 输入验证(try-except)
  • ✅ 循环功能
  • ✅ 格式化输出
  • ✅ 退出功能

练习4:改进你自己的程序

从你之前写过的程序中选择一个(例如:

  • 长方形计算器(第2章)
  • 温度转换器(第4章)
  • 水仙花数程序(第8章)
  • 课表查询系统(第14章)

按照以下步骤进行改进:

  1. 评价原始程序

    • 用五个维度评价
    • 列出所有问题
  2. 设计改进方案

    • 改进变量命名
    • 添加输入验证
    • 改进用户界面
    • 添加错误处理
  3. 重写程序

    • 应用本章学到的知识
    • 使用函数封装
    • 添加注释
  4. 测试程序

    • 设计测试用例
    • 验证各种情况

17.5 常见错误和调试

常见的程序设计错误

错误1:过早优化(Premature Optimization)

表现

  • 还没让程序运行起来,就开始考虑“怎么让它更快“
  • 为了“效率“牺牲了可读性

例子

# ❌ 过早优化的例子
# 为了节省一行代码,牺牲了可读性
result = (lambda x: x * x)(int(input("输入数字:")))
print(result)

# ✅ 先让它能运行,再考虑优化
user_input = input("输入数字:")
number = int(user_input)
result = number * number
print(result)

建议

  • “Make it work, make it right, make it fast”
  • 先让它能运行,再让它运行正确,最后才考虑让它运行得快
  • 对于初学者,可读性 > 效率

错误2:过度设计(Over-Engineering)

表现

  • 为简单的问题设计复杂的解决方案
  • “用大炮打蚊子”

例子

# ❌ 过度设计的例子
# 一个简单的加法程序,却用了复杂的架构

class NumberAdder:
    """数字加加器类"""
    def __init__(self):
        self.numbers = []

    def add_number(self, number):
        self.numbers.append(number)

    def calculate_sum(self):
        return sum(self.numbers)

def main():
    adder = NumberAdder()
    adder.add_number(10)
    adder.add_number(20)
    result = adder.calculate_sum()
    print(result)

main()

# ✅ 简单直接的设计
num1 = 10
num2 = 20
result = num1 + num2
print(result)

建议

  • 简单问题用简单方案
  • 只在需要时才引入复杂结构
  • KISS原则:Keep It Simple, Stupid

错误3:重复代码(Code Duplication)

表现

  • 相同的代码在程序中出现多次
  • 不知道如何使用函数

例子

# ❌ 重复代码
area1 = length1 * width1
area2 = length2 * width2
area3 = length3 * width3
area4 = length4 * width4

# ✅ 使用函数
def calculate_area(length, width):
    return length * width

area1 = calculate_area(length1, width1)
area2 = calculate_area(length2, width2)
area3 = calculate_area(length3, width3)
area4 = calculate_area(length4, width4)

建议

  • 遵循DRY原则:Don’t Repeat Yourself
  • 相同的代码出现3次,就该考虑用函数

错误4:魔法数字(Magic Numbers)

表现

  • 代码中出现没有解释的数字
  • 不知道这些数字是什么意思

例子

# ❌ 魔法数字
area = 3.14159 * radius * radius
if score >= 60:
    print("及格")

# ✅ 使用命名常量
PI = 3.14159
PASSING_SCORE = 60

area = PI * radius * radius
if score >= PASSING_SCORE:
    print("及格")

建议

  • 给重要的数字起个名字
  • 使用全大写字母命名常量
  • 常量定义在程序开头

错误5:忽略错误处理(No Error Handling)

表现

  • 假设用户总是输入正确
  • 程序遇到错误就崩溃

例子

# ❌ 没有错误处理
age = int(input("请输入年龄:"))
print(f"你明年{age + 1}岁")

# ✅ 添加错误处理
try:
    age = int(input("请输入年龄:"))
    if age < 0 or age > 150:
        print("年龄应该在0-150之间")
    else:
        print(f"你明年{age + 1}岁")
except ValueError:
    print("请输入有效的数字!")

建议

  • 永远不要相信用户输入
  • 使用try-except捕获异常
  • 验证输入的有效性

调试技巧

技巧1:使用print调试

方法:在关键位置打印变量值

def calculate_average(scores):
    print(f"[DEBUG] scores = {scores}")  # 调试输出

    if not scores:
        print("[DEBUG] Empty list!")
        return 0

    total = sum(scores)
    print(f"[DEBUG] total = {total}")  # 调试输出

    count = len(scores)
    print(f"[DEBUG] count = {count}")  # 调试输出

    average = total / count
    print(f"[DEBUG] average = {average}")  # 调试输出

    return average

优点

  • 简单直接
  • 不需要额外工具
  • 适合快速定位问题

缺点

  • 需要手动添加和删除
  • 调试完成后要清理代码

给家长的小贴士

  • 教导孩子:“不知道哪里出错,就打印出来看看”
  • 建议使用统一的格式,如[DEBUG]
  • 调试完成后记得删除或注释掉

技巧2:逐步测试

方法:不要等程序全部写完才测试,每写一段就测试

# 第1步:测试基本功能
def calculate_area(length, width):
    return length * width

# 立即测试
print(calculate_area(10, 20))  # 应该输出200

# 第2步:添加输入验证
def calculate_area(length, width):
    if length <= 0 or width <= 0:
        return 0
    return length * width

# 再次测试
print(calculate_area(10, 20))    # 200
print(calculate_area(-10, 20))  # 0

# 第3步:添加文档和优化
def calculate_area(length, width):
    """计算长方形面积

    Args:
        length: 长度
        width: 宽度

    Returns:
        面积,如果输入无效则返回0
    """
    if length <= 0 or width <= 0:
        return 0
    return length * width

优点

  • 问题容易定位
  • 每一步都是正确的
  • 积累成就感

给家长的小贴士

  • 鼓励孩子“写一点,测一点“
  • 不要等到全部写完才运行
  • 这能培养孩子的“增量开发“习惯

技巧3:使用断言(Assertion)

方法:用assert语句检查假设

def calculate_average(scores):
    """计算平均分"""
    assert isinstance(scores, list), "scores必须是列表"
    assert len(scores) > 0, "scores不能为空"

    total = sum(scores)
    assert total >= 0, "总分不能为负数"

    average = total / len(scores)
    assert 0 <= average <= 100, f"平均分{average}超出范围"

    return average

# 测试
print(calculate_average([80, 90, 85]))  # 正常
# print(calculate_average([]))          # 触发断言错误

优点

  • 能快速发现逻辑错误
  • 文档化代码的假设
  • 调试后可以选择禁用

使用场景

  • 检查函数的输入
  • 验证计算结果
  • 确保不变式成立

技巧4:简化问题

方法:遇到复杂问题时,先用简单版本复现

# 复杂版本:计算n个班级的平均分
def calculate_all_classes_average(students):
    result = {}
    for class_name, class_students in students.items():
        # 复杂的计算...
        pass

# 简化版本:先计算一个班级
def calculate_one_class_average(scores):
    return sum(scores) / len(scores)

# 测试简化版本
print(calculate_one_class_average([80, 90, 85]))  # 确保正确

# 再逐步扩展到复杂版本

优点

  • 降低问题复杂度
  • 容易找到问题所在
  • 逐步构建解决方案

给家长的小贴士

  • 告诉孩子:“太复杂了就先简化”
  • 从最简单的情况开始
  • 这能培养孩子的“问题分解“能力

17.6 本章小结

核心知识点回顾

1. 程序评价的五个维度

维度评价标准重要性
正确性能否正确解决问题⭐⭐⭐⭐⭐
可读性是否容易理解⭐⭐⭐⭐
可维护性是否容易修改⭐⭐⭐⭐
健壮性是否容易出错⭐⭐⭐⭐
效率是否运行快速⭐⭐⭐

2. 变量设计的三个原则

  1. 职责明确:一个变量只承担一个职责
  2. 命名规范:变量名要能“自解释“
  3. 作用域最小化:变量的作用范围越小越好

3. 程序设计的五个步骤

第1步:明确需求(做什么)
   ↓
第2步:设计输入输出(怎么交互)
   ↓
第3步:设计主要步骤(怎么做)
   ↓
第4步:设计数据结构(用什么数据)
   ↓
第5步:设计验证方法(怎么验证)

4. 常见错误

  • ❌ 过早优化
  • ❌ 过度设计
  • ❌ 重复代码
  • ❌ 魔法数字
  • ❌ 忽略错误处理

5. 调试技巧

  • ✅ 使用print调试
  • ✅ 逐步测试
  • ✅ 使用断言
  • ✅ 简化问题

能力检查表

完成本章学习后,请检查你是否具备以下能力:

  • 能用五个维度评价一个程序的好坏
  • 能设计职责明确、命名规范的变量
  • 能按照五个步骤设计程序
  • 能使用伪代码和流程图设计算法
  • 能改进自己之前写的程序
  • 能识别和避免常见的程序设计错误
  • 能使用调试技巧定位问题

给家长的小贴士:如何培养孩子的程序设计能力

1. 从小程序开始

不要:一开始就要求孩子写出“完美“的程序

应该

  • 先让程序能运行
  • 再逐步改进
  • 最后追求完美

比喻:学写作文

  • 小学1年级:写通顺句子
  • 小学3年级:写完整段落
  • 小学6年级:写完整文章
  • 不能要求1年级学生写出6年级水平的作文

2. 培养评价能力

活动建议

  • 让孩子“代码审查“:找出家长写的程序的问题
  • 对比两个程序:哪个更好?为什么?
  • 重构练习:改进之前写的程序

目的

  • 培养“批判性思维“
  • 学会识别“好代码“和“坏代码“
  • 建立“质量意识“

3. 强调设计过程

不要:看到题目就让孩子直接写代码

应该

  • 先讨论:这个程序要做什么?
  • 再设计:用什么数据结构?什么算法?
  • 最后实现:编写代码
  • 测试验证:是否正确?

比喻

  • 建房子:先设计图纸,再施工
  • 写作文:先列提纲,再写作
  • 编程也是一样:先设计,再编程

4. 鼓励持续改进

活动建议

  • 定期回顾:这周写的程序,下周再看,能不能改进?
  • 版本对比:版本1、版本2、版本3,哪个最好?
  • 最佳实践:总结好的做法,形成“编程规范“

目的

  • 培养“精益求精“的态度
  • 学会从“能运行“到“写得好“
  • 建立“持续改进“的习惯

17.7 挑战练习

挑战1:代码审查 ⭐⭐

审查以下程序,找出所有问题并改进:

a = input("名字")
b = int(input("年龄"))
c = int(input("成绩"))
if c >= 60:
    print("及格")
else:
    print("不及格")
print(c / b)

要求

  1. 用五个维度评价
  2. 指出所有问题
  3. 重写程序
  4. 说明改进点
📝 查看参考答案

评价结果

维度评分问题
正确性⭐⭐⭐基本正确,但除法可能有问题
可读性变量名不清晰,无注释
可维护性⭐⭐无函数封装
健壮性无输入验证
效率⭐⭐⭐⭐效率可以接受

问题列表

  1. 变量名a, b, c不清晰
  2. 没有提示信息
  3. 没有输入验证
  4. 成绩除以年龄没有意义(可能是bug)
  5. 没有注释
  6. 没有函数封装

改进版本

def print_student_status():
    """打印学生状态"""

    # 获取学生信息
    name = input("请输入学生姓名:")
    age = input("请输入学生年龄:")
    score = input("请输入学生成绩:")

    # 验证输入
    if not age.isdigit() or not score.isdigit():
        print("错误:年龄和成绩必须是数字!")
        return

    age = int(age)
    score = int(score)

    # 验证范围
    if age < 0 or age > 150:
        print("错误:年龄应该在0-150之间!")
        return

    if score < 0 or score > 100:
        print("错误:成绩应该在0-100之间!")
        return

    # 判断是否及格
    if score >= 60:
        status = "及格"
    else:
        status = "不及格"

    # 输出结果
    print(f"\n=== 学生信息 ===")
    print(f"姓名:{name}")
    print(f"年龄:{age}岁")
    print(f"成绩:{score}分")
    print(f"状态:{status}")

if __name__ == "__main__":
    print_student_status()

改进点

  • ✅ 清晰的变量命名
  • ✅ 完整的输入提示
  • ✅ 输入验证(数字、范围)
  • ✅ 格式化输出
  • ✅ 函数封装
  • ✅ 注释说明

挑战2:重构程序 ⭐⭐⭐

重构以下程序,应用本章学到的知识:

l = 10
w = 20
h = 5
s1 = l * w
s2 = l * h
s3 = w * h
print(s1, s2, s3)
print(s1 + s2 + s3)

要求

  1. 给变量起好名字
  2. 添加注释
  3. 封装成函数
  4. 添加输入验证
  5. 改进输出格式
📝 查看参考答案
def calculate_surface_area(length, width, height):
    """计算长方体的表面积

    Args:
        length: 长度
        width: 宽度
        height: 高度

    Returns:
        表面积
    """
    # 计算三个面的面积
    area1 = length * width   # 顶面和底面
    area2 = length * height  # 前面和后面
    area3 = width * height   # 左面和右面

    # 表面积 = 2 × (三个面的面积之和)
    surface_area = 2 * (area1 + area2 + area3)

    return surface_area

def get_positive_number(prompt):
    """获取一个正数

    Args:
        prompt: 输入提示

    Returns:
        用户输入的正数
    """
    while True:
        user_input = input(prompt)

        if not user_input.isdigit():
            print("请输入一个有效的数字!")
            continue

        number = float(user_input)

        if number <= 0:
            print("请输入一个大于0的数字!")
            continue

        return number

def main():
    """主程序"""
    print("=== 长方体表面积计算器 ===")

    # 获取长宽高
    length = get_positive_number("请输入长度:")
    width = get_positive_number("请输入宽度:")
    height = get_positive_number("请输入高度:")

    # 计算表面积
    surface_area = calculate_surface_area(length, width, height)

    # 显示结果
    print(f"\n=== 计算结果 ===")
    print(f"长方体尺寸:{length} × {width} × {height}")
    print(f"表面积:{surface_area:.2f}")

if __name__ == "__main__":
    main()

挑战3:设计程序 ⭐⭐⭐⭐

按照程序设计的五个步骤,设计一个“密码强度检查器“:

功能需求

  • 输入密码
  • 检查密码强度(弱/中/强)
  • 显示改进建议
  • 直到输入强密码为止

要求

  1. 完成五个步骤的设计
  2. 实现完整程序
  3. 编写测试用例
  4. 提供改进建议

强度判断标准

  • 弱:长度<8或只有数字或只有字母
  • 中:长度>=8且包含数字和字母
  • 强:长度>=12且包含数字、字母和特殊字符
📝 查看参考答案

第1步:明确需求

功能需求:
1. 输入密码
2. 检查密码强度
3. 显示强度等级
4. 提供改进建议
5. 循环直到输入强密码

输入:
- 密码(字符串)

输出:
- 强度等级(弱/中/强)
- 改进建议

边界情况:
- 空字符串
- 只有数字
- 只有字母
- 包含特殊字符

第2步:设计输入输出

=== 密码强度检查器 ===
请输入密码:******

强度:弱
建议:
- 密码长度至少8位
- 包含数字和字母
- 包含特殊字符

请重新输入密码:******

强度:强
✓ 密码符合要求!

第3步:设计主要步骤(伪代码)

main:
    循环:
        输入密码
        检查强度
        显示强度和建议
        如果是强密码:
            退出循环

check_password_strength:
    如果长度<8:
        返回"弱"
    如果只有数字或只有字母:
        返回"弱"
    如果长度>=8且包含数字和字母:
        返回"中"
    如果长度>=12且包含数字、字母和特殊字符:
        返回"强"

get_suggestions:
    根据密码的问题提供建议
    - 如果太短,建议增加长度
    - 如果只有数字,建议添加字母
    - 如果只有字母,建议添加数字
    - 如果没有特殊字符,建议添加特殊字符

第4步:设计数据结构

password = ""           # 密码(字符串)
length = 0             # 长度(整数)
has_digit = False      # 包含数字(布尔)
has_letter = False     # 包含字母(布尔)
has_special = False    # 包含特殊字符(布尔)
strength = ""          # 强度(字符串)
suggestions = []       # 建议列表

第5步:设计验证方法

测试用例1:弱密码
输入:123
预期:弱(太短)

测试用例2:弱密码
输入:abcdefg
预期:弱(太短且只有字母)

测试用例3:弱密码
输入:12345678
预期:弱(只有数字)

测试用例4:中等密码
输入:abc12345
预期:中

测试用例5:强密码
输入:abc123!@#XYZ
预期:强

完整实现

def check_password_strength(password):
    """检查密码强度

    Args:
        password: 要检查的密码

    Returns:
        (strength, suggestions): 强度等级和建议列表
    """
    suggestions = []

    # 检查长度
    if len(password) < 8:
        suggestions.append("密码长度至少8位")
        return "弱", suggestions

    # 检查是否包含数字
    has_digit = any(char.isdigit() for char in password)

    # 检查是否包含字母
    has_letter = any(char.isalpha() for char in password)

    # 检查是否包含特殊字符
    has_special = any(not char.isalnum() for char in password)

    # 判断强度
    if not has_digit or not has_letter:
        suggestions.append("密码应同时包含数字和字母")
        if not has_digit:
            suggestions.append("建议添加数字")
        if not has_letter:
            suggestions.append("建议添加字母")
        return "弱", suggestions

    if len(password) >= 12 and has_special:
        suggestions.append("密码很安全!")
        return "强", suggestions

    if len(password) >= 8 and has_digit and has_letter:
        suggestions.append("建议增加到12位以上")
        suggestions.append("建议添加特殊字符(如!@#)")
        return "中", suggestions

    return "弱", ["密码不符合要求"]

def password_strength_checker():
    """密码强度检查器主程序"""
    print("=== 密码强度检查器 ===")
    print("密码要求:")
    print("- 长度至少8位")
    print("- 包含数字和字母")
    print("- 强密码要求12位以上且包含特殊字符")
    print()

    while True:
        password = input("请输入密码(输入q退出):")

        if password.lower() == 'q':
            print("已退出")
            break

        if not password:
            print("密码不能为空!\n")
            continue

        # 检查强度
        strength, suggestions = check_password_strength(password)

        # 显示结果
        print(f"\n强度:{strength}")

        if strength == "强":
            print("✓ " + "\n✓ ".join(suggestions))
            print()
            break
        else:
            print("建议:")
            for suggestion in suggestions:
                print(f"- {suggestion}")
            print()

def test_password_checker():
    """测试密码强度检查器"""
    test_cases = [
        ("123", "弱"),
        ("abcdefg", "弱"),
        ("12345678", "弱"),
        ("abc12345", "中"),
        ("abc123!@#XYZ", "强"),
    ]

    print("=== 测试密码强度检查器 ===")
    for password, expected in test_cases:
        strength, _ = check_password_strength(password)
        status = "✓" if strength == expected else "✗"
        print(f"{status} '{password}' -> {strength} (期望: {expected})")

if __name__ == "__main__":
    # 运行测试
    test_password_checker()
    print()

    # 运行主程序
    password_strength_checker()

挑战4:改进你自己的程序 ⭐⭐⭐⭐⭐

选择你之前写过的一个程序(例如:课表查询系统、猜数字游戏、水仙花数等),按照以下步骤进行改进:

  1. 评价原始程序

    • 用五个维度评价
    • 列出所有问题
  2. 设计改进方案

    • 改进变量命名
    • 添加输入验证
    • 改进用户界面
    • 添加错误处理
    • 使用函数封装
  3. 重写程序

    • 应用本章学到的知识
    • 添加注释
    • 改进结构
  4. 对比版本

    • 列出改进前后的对比
  • 说明改进理由
  1. 编写测试
    • 设计测试用例
    • 验证各种情况

第18章 计算机体系结构

本章导读

在前面的章节中,我们学习了Python编程的各个方面:从变量和函数,到图形界面和数据处理。你可能会有疑问:

  • 计算机是如何执行这些Python代码的?
  • 变量存储在哪里?
  • 当我们输入数据时,计算机内部发生了什么?

这一章,我们将打开计算机的“外壳“,了解它内部是如何工作的。我们会发现,之前学过的很多知识,都与计算机的内部结构密切相关!

什么是计算机体系结构?

计算机体系结构是指计算机的组成部件以及这些部件如何协同工作。

想象一下,你的身体就是一个“计算机“:

  • 大脑 = CPU(中央处理器),负责思考和做决定
  • 眼睛/耳朵 = 输入设备,接收外界信息
  • 嘴巴/手势 = 输出设备,向外界表达信息
  • 记忆 = 内存和硬盘,存储信息
  • 神经 = 总线,在各个部件之间传递信息

👨‍🏫 给家长的Tips

用人体做类比非常适合孩子理解:

  • 问孩子:“如果我想踢足球,大脑会给腿发送什么指令?”(控制信号)
  • “你记得的事情,哪些是短期的?哪些是长期的?”(内存 vs 硬盘)
  • “如果你不复习,会忘记学过的内容吗?”(内存的易失性)

计算机的五大组成部分

计算机由五个主要部分组成:

flowchart TB
    subgraph 计算机系统
        InputDevice[输入设备<br/>Input Device]
        CPU[CPU<br/>中央处理器]
        Memory[内存<br/>Memory]
        OutputDevice[输出设备<br/>Output Device]
        Storage[硬盘<br/>Storage]

        InputDevice -->|数据| CPU
        CPU <-->|数据/指令| Memory
        CPU -->|结果| OutputDevice
        CPU <-->|数据| Storage
    end

    style InputDevice fill:#e1f5ff
    style CPU fill:#ffe1e1
    style Memory fill:#e1ffe1
    style OutputDevice fill:#fff5e1
    style Storage fill:#f5e1ff

让我们逐一了解每个部分,并联系我们已经学过的Python知识!

1. 输入设备(Input Devices)

输入设备是让计算机接收信息的设备。

常见的输入设备

  • 键盘:输入文字和命令
  • 鼠标:点击和移动
  • 触摸屏:手指操作
  • 麦克风:输入声音
  • 摄像头:输入图像

与我们学过的知识联系

还记得我们学过的输入程序吗?

第2章:输入与输出

name = input("请输入你的名字:")
print(f"你好,{name}!")

当我们运行这个程序时:

  1. 键盘(输入设备)接收你敲击的名字
  2. 通过总线传给CPU
  3. CPU处理后将结果传给显卡
  4. 显示器(输出设备)显示“你好,xxx!“

第14章:命令行程序

import sys

name = sys.argv[1]  # 从命令行接收参数
print(f"你好,{name}!")

这里,命令行参数也是一种输入,通过键盘输入命令传递给程序。

第16章:图形界面程序

import tkinter as tk

def on_click():
    name = entry.get()  # 从输入框获取文本
    label.config(text=f"你好,{name}!")

root = tk.Tk()
entry = tk.Entry(root)  # 输入框
entry.pack()
button = tk.Button(root, text="提交", command=on_click)
button.pack()
label = tk.Label(root, text="")
label.pack()
root.mainloop()

在这个GUI程序中:

  • 输入框(Entry组件)是屏幕上的“虚拟输入设备“
  • 你在输入框输入字符,这些字符被输入设备接收
  • 点击按钮后,程序获取输入框的内容

👨‍🏫 给家长的Tips

引导孩子思考:

  • “无论是命令行还是图形界面,都需要输入设备。为什么图形界面更友好?”
  • “触摸屏既是输入设备又是输出设备,你知道吗?”
  • “想象一下,如果没有输入设备,我们能和计算机交流吗?”

2. 输出设备(Output Devices)

输出设备是计算机向外界展示信息的设备。

常见的输出设备

  • 显示器:显示图像和文字
  • 打印机:打印纸质文档
  • 扬声器:播放声音
  • 投影仪:投射大屏幕

与我们学过的知识联系

第2章:print输出

print("Hello, World!")

当我们执行print()时:

  • CPU计算要显示的内容
  • 通过总线将数据传给显卡
  • 显卡将数据转换为显示信号
  • 显示器接收信号,显示文字

第15章:报表程序

import matplotlib.pyplot as plt

x = [1, 2, 3, 4, 5]
y = [10, 20, 15, 25, 30]

plt.plot(x, y)
plt.show()  # 显示图表

这里的图表显示也是输出

  • matplotlib库生成图表数据
  • 通过显示器展示给你看

第16章:图形界面

label.config(text="你好!")

在GUI程序中:

  • 组件(Label、Button等)显示在显示器
  • 它们的变化(文字、颜色等)都是输出

练习1:分类游戏

将下列设备分类为输入设备、输出设备,或两者都是:

  • 键盘
  • 鼠标
  • 显示器
  • 打印机
  • 扫描仪
  • 摄像头
  • 音箱
  • 触摸屏
👉 点击查看答案

答案

  • 输入设备:键盘、鼠标、扫描仪、摄像头
  • 输出设备:显示器、打印机、音箱
  • 两者都是:触摸屏(既是输入又是输出)

3. 内存(Memory/RAM)

内存(Random Access Memory,随机存取存储器)是计算机的短期记忆

内存的特点

  • 速度快:CPU读写内存非常快
  • 容量有限:通常8GB、16GB、32GB等
  • 易失性:断电后数据会丢失

与我们学过的知识联系

还记得我们学过的变量吗?

第3-5章:变量(字符串、数字、布尔值)

name = "小明"       # 字符串变量
age = 10           # 数字变量
is_student = True  # 布尔变量

当我们创建这些变量时:

  • 变量存储在内存
  • 每个变量都有一个内存地址
  • Python自动管理内存,我们不需要手动分配

👨‍🏫 给家长的Tips

用“书包“类比内存:

  • 书包空间有限(内存容量有限)
  • 随时可以拿书出来用(读写快)
  • 回家后书包里的书要拿走(易失性,断电数据丢失)
  • 家里的书柜就像硬盘(容量大,永久存储)

练习2:计算内存占用

不同类型的变量占用内存不同(大致数值):

变量类型示例占用内存(估算)
整数(int)age = 10约28字节
浮点数(float)price = 3.14约24字节
字符串(str)name = “Python”约50字节
布尔值(bool)flag = True约28字节
列表(list)[1, 2, 3]约56字节 + 元素大小

问题:如果你的计算机有16GB内存,大约可以存储多少个整数变量?

👉 点击查看答案

解答

  1. 16GB = 16 × 1024MB = 16,384MB
  2. 16,384MB = 16,777,216KB
  3. 16,777,216KB = 17,179,869,184字节
  4. 每个整数约28字节
  5. 可存储的整数数量 ≈ 17,179,869,184 ÷ 28 ≈ 613,566,756个

也就是说,16GB内存可以存储约6亿个整数变量!

思考:虽然可以存储这么多变量,但实际编程时我们不会创建这么多。为什么?

答案:因为程序还有其他数据需要存储,而且变量太多会让程序难以管理。

第10-11章:列表和字典

# 列表
scores = [90, 85, 78, 92, 88]

# 字典
student = {"name": "小明", "age": 10, "grade": "五年级"}

列表和字典也存储在内存中:

  • 它们占用更多内存(因为要存储多个元素)
  • 当列表很大时(如100万个元素),会占用大量内存

练习3:大列表的内存占用

如果一个列表存储100万个学生的成绩,大约占用多少内存?

👉 点击查看答案

解答

  1. 每个整数(成绩)约28字节
  2. 列表本身还有一些额外开销
  3. 总内存 ≈ 1,000,000 × 28字节 ≈ 28,000,000字节 ≈ 28MB

100万个成绩占用约28MB内存,这对于16GB的计算机来说只是很小的一部分!

4. 硬盘(Storage/Hard Drive)

硬盘是计算机的长期记忆

硬盘的特点

  • 容量大:通常500GB、1TB、2TB等
  • 速度慢:比内存慢得多
  • 非易失性:断电后数据仍然保存

与我们学过的知识联系

还记得我们学过的文件操作吗?

第13章:JSON文件存储

import json

# 保存课表到文件
schedule = {
    "周一": ["语文", "数学", "英语"],
    "周二": ["数学", "语文", "体育"]
}

with open("schedule.json", "w", encoding="utf-8") as f:
    json.dump(schedule, f)

# 从文件读取课表
with open("schedule.json", "r", encoding="utf-8") as f:
    loaded_schedule = json.load(f)

在这个过程中:

  1. schedule字典存储在内存
  2. json.dump()将数据写入硬盘(schedule.json文件)
  3. 关闭程序后,数据仍然保存在硬盘上
  4. 下次运行时,用json.load()从硬盘读取数据

👨‍🏫 给家长的Tips

用“笔记本“类比硬盘:

  • 笔记本可以记很多东西(容量大)
  • 写字速度比脑子想得慢(速度慢)
  • 合上本子,内容还在(非易失性)
  • 想复习时,打开本子就能看到(读取数据)

内存 vs 硬盘对比

特性内存(RAM)硬盘(Storage)
速度非常快较慢
容量较小(8-32GB)很大(500GB-2TB)
价格较贵较便宜
断电后数据丢失数据保留
用途存储正在运行的程序和数据永久存储文件

为什么需要两者?

想象一下,如果只有硬盘,没有内存:

  • 每次操作都要读写硬盘,速度会非常慢
  • 计算机运行会变得很卡

如果只有内存,没有硬盘:

  • 关机后所有数据都会丢失
  • 无法保存照片、文档等文件

练习4:估算硬盘容量

如果一张照片占3MB空间,1TB的硬盘可以存储多少张照片?

👉 点击查看答案

解答

  1. 1TB = 1,024GB = 1,048,576MB
  2. 可存储照片数 = 1,048,576MB ÷ 3MB ≈ 349,525张

1TB硬盘可以存储约35万张照片!

5. CPU(中央处理器)

CPU(Central Processing Unit,中央处理器)是计算机的大脑,负责执行所有计算和控制操作。

CPU的特点

  • 速度快:以GHz为单位(如3.2GHz,表示每秒执行32亿个周期)
  • 智能:能执行复杂的计算和逻辑判断
  • 控制中心:指挥其他所有部件协同工作

与我们学过的知识联系

第4章:数学运算

result = 5 + 3  # 加法
result = 10 * 2  # 乘法
result = 20 / 4  # 除法

当我们执行这些运算时:

  • CPU的**算术逻辑单元(ALU)**执行计算
  • 每次运算只需要几个CPU周期(几纳秒)

练习5:计算CPU运算能力

如果你的CPU主频是3.2GHz,它1秒内可以执行多少次加法运算?

👉 点击查看答案

解答

  1. 3.2GHz = 3,200,000,000 Hz(每秒32亿个周期)
  2. 假设每次加法需要1个周期(简化计算)
  3. 1秒可以执行约32亿次加法运算!

人类1秒可能只能做1-2次加法,而CPU可以做32亿次!这就是为什么计算机这么快!

第6-8章:程序控制(顺序、条件、循环)

# 顺序执行
a = 5
b = 10
c = a + b

# 条件判断
if score >= 60:
    print("及格")
else:
    print("不及格")

# 循环
for i in range(100):
    print(i)

CPU如何执行这些程序?

  1. 取指令:从内存中读取程序指令
  2. 译码:理解指令要做什么
  3. 执行:执行指令(计算、判断、跳转等)
  4. 写回:将结果写回内存

这个过程每秒重复几十亿次!

👨‍🏫 给家长的Tips

用“厨师“类比CPU:

  • 厨师根据菜谱(程序)做菜
  • 按照菜谱的步骤(顺序执行)
  • 根据情况调整(条件判断)
  • 重复切菜动作(循环)
  • 厨师的速度越快,做菜越快(CPU主频)

多核CPU

现代CPU通常有多个“核心“(core),如4核、6核、8核。

  • 单核:一次只能做一件事
  • 多核:可以同时做多件事(并行处理)
# 模拟多核处理(实际需要使用多进程/多线程库)
# 这个例子只是概念演示
def task1():
    for i in range(1000):
        result = i * i

def task2():
    for j in range(1000):
        result = j + j

# 单核:先执行task1,再执行task2
# 多核:task1和task2可以同时执行

练习6:多核的优势

如果CPU有4个核心,处理4个任务(每个任务需要1秒):

  • 单核CPU需要多少秒?
  • 4核CPU需要多少秒?
👉 点击查看答案

解答

  • 单核CPU:4秒(因为只能一个接一个执行)
  • 4核CPU:1秒(因为可以同时执行4个任务)

这就是为什么多核CPU更快!

计算机如何执行Python程序?

现在我们了解了计算机的各个部件,让我们看看它们如何协同工作来执行一个Python程序。

示例:计算平均分

# 程序:计算三个成绩的平均分
score1 = 90
score2 = 85
score3 = 78
average = (score1 + score2 + score3) / 3
print(f"平均分是:{average}")

执行过程

flowchart TD
    subgraph 硬盘
        A[Python程序文件]
    end

    subgraph 内存
        B[程序载入内存]
        C1[score1 = 90]
        C2[score2 = 85]
        C3[score3 = 78]
        C4[average = 84.333...]
    end

    subgraph CPU
        D1[第1行: 分配内存存储90]
        D2[第2行: 分配内存存储85]
        D3[第3行: 分配内存存储78]
        D4[第4行: 读取score1, score2, score3<br/>执行加法和除法运算<br/>存储结果到average]
        D5[第5行: 读取average<br/>传数据给显卡]
    end

    subgraph 输出设备
        E[显卡/显示器]
    end

    A -->|1. 从硬盘读取程序到内存| B
    B -->|2. CPU逐条读取并执行指令| D1
    D1 -->|在内存中分配| C1
    D1 --> D2
    D2 -->|在内存中分配| C2
    D2 --> D3
    D3 -->|在内存中分配| C3
    D3 --> D4
    D4 -->|从内存读取| C1
    D4 -->|从内存读取| C2
    D4 -->|从内存读取| C3
    D4 -->|将结果存入| C4
    D4 --> D5
    D5 -->|从内存读取| C4
    D5 -->|通过总线传数据| E
    E -->|显示文字| F["显示: 平均分是:84.333..."]

    style 硬盘 fill:#e1f5ff
    style 内存 fill:#e1ffe1
    style CPU fill:#ffe1e1
    style 输出设备 fill:#fff5e1

👨‍🏫 给家长的Tips

这个流程图非常重要!

  • 让孩子理解:程序不是“一次性“执行的,而是“一步一步“执行的
  • 每一步都需要CPU、内存、输入/输出设备的协同工作
  • 可以用“做菜流程“类比:买菜→洗菜→切菜→炒菜→装盘

综合练习:设计你的计算机

现在你已经了解了计算机的各个部件,让我们设计一台计算机!

练习7:计算机配置选择

假设你要买一台计算机用于学习Python,请选择合适的配置:

部件选项A选项B选项C你的选择原因
CPU双核2.0GHz四核3.0GHz八核4.0GHz
内存8GB16GB32GB
硬盘256GB SSD512GB SSD1TB SSD
输入设备标准键盘机械键盘游戏键盘
输出设备普通显示器高清显示器4K显示器
👉 点击查看参考答案

参考答案

部件推荐选择原因
CPU四核3.0GHz学习Python足够,性价比高
内存16GB可以运行多个程序,不会卡顿
硬盘512GB SSD存储课程和作业足够,SSD速度快
输入设备机械键盘手感好,适合长时间编程
输出设备高清显示器清晰舒适,保护眼睛

注意:这只是学习Python的配置。如果要玩大型游戏或做视频剪辑,需要更高的配置。

练习8:连接知识点

让我们把之前学过的Python知识和计算机硬件联系起来:

  1. 变量(第3-5章)存储在_____
  2. 文件(第13章)存储在_____
  3. **print()**输出到_____(设备)
  4. **input()**从_____(设备)接收
  5. 运算(第4章)由_____执行
  6. 条件判断(第7章)由_____的_____单元执行
  7. 循环(第8章)由_____控制重复执行
👉 点击查看答案

答案

  1. 内存
  2. 硬盘
  3. 显示器(输出设备)
  4. 键盘(输入设备)
  5. CPU
  6. CPU的算术逻辑单元(ALU)
  7. CPU

练习9:创意项目

设计一个Python程序,展示你对计算机硬件的理解!

项目想法

  • 计算机硬件测验:用GUI设计一个测验程序,问用户关于计算机硬件的问题
  • 硬件信息显示:用Python读取并显示你计算机的硬件信息(CPU型号、内存大小等)
  • 资源监控器:编写一个程序,实时显示CPU和内存使用情况

示例框架

import tkinter as tk
import psutil  # 需要安装:pip install psutil

def show_info():
    cpu_percent = psutil.cpu_percent()
    memory = psutil.virtual_memory()
    mem_percent = memory.percent

    info_text = f"""
    计算机硬件信息:
    ─────────────────
    CPU使用率:{cpu_percent}%
    内存使用率:{mem_percent}%
    内存总量:{memory.total / (1024**3):.1f} GB
    已用内存:{memory.used / (1024**3):.1f} GB
    """
    label.config(text=info_text)

root = tk.Tk()
root.title("计算机硬件信息")

button = tk.Button(root, text="刷新信息", command=show_info)
button.pack()

label = tk.Label(root, text="点击按钮查看信息", justify="left")
label.pack()

root.mainloop()

👨‍🏫 给家长的Tips

这个项目需要安装psutil库:

  • 在命令行输入:pip install psutil
  • 这是一个很好的实践项目,让孩子看到真实的硬件数据
  • 可以问孩子:“为什么CPU使用率会变化?”
  • “运行什么程序会让CPU使用率升高?”

本章小结

我们学到了什么?

  1. 计算机的五大组成部分

    • 输入设备:键盘、鼠标等
    • 输出设备:显示器、打印机等
    • 内存:短期存储,速度快但易失
    • 硬盘:长期存储,容量大但速度慢
    • CPU:大脑,执行计算和控制
  2. Python知识与硬件的联系

    • 变量 → 内存
    • 文件 → 硬盘
    • print() → 输出设备(显示器)
    • input() → 输入设备(键盘)
    • 运算 → CPU的ALU
    • 控制结构 → CPU的控制单元
  3. 程序执行过程

    • 硬盘 → 内存 → CPU → 输出设备
    • 所有部件协同工作

重要概念对比

概念计算机硬件Python对应
短期记忆内存(RAM)变量、列表、字典
长期记忆硬盘文件、JSON
思考CPU运算、条件判断、循环
感觉输入设备input()
表达输出设备print()

给家长的辅导建议

  1. 实物演示

    • 如果可以,打开一台旧电脑,让孩子看到真实的硬件部件
    • 指着内存条、硬盘、CPU,讲解它们的作用
  2. 类比教学

    • 人体类比:大脑=CPU,记忆=内存/硬盘,感官=输入/输出
    • 厨房类比:厨师=CPU,菜谱=程序,食材=数据,锅碗瓢盆=内存
  3. 联系实际

    • 当孩子写程序时,问:“这个变量存在哪里?”
    • “为什么关闭程序后数据会丢失?”
    • “为什么要保存文件?”
  4. 扩展思考

    • “如果计算机没有内存,只有硬盘,会怎样?”
    • “如果CPU速度慢10倍,使用计算机的体验会有什么不同?”
    • “未来的计算机会是什么样子?”

下一步

恭喜你完成了这本书的所有章节!

你已经从零开始,学会了:

  • ✅ Python基础语法(变量、运算、数据类型)
  • ✅ 程序控制结构(顺序、条件、循环)
  • ✅ 数据结构(列表、字典)
  • ✅ 函数和库
  • ✅ 文件操作
  • ✅ 命令行程序、图形界面、数据报表
  • ✅ 计算机体系结构

接下来的学习建议

  1. 继续练习:编程需要多动手,找一些小项目做
  2. 学习更多库:如游戏开发(Pygame)、网站开发(Flask)、数据分析(Pandas)
  3. 参加竞赛:如NOI(全国青少年信息学奥林匹克竞赛)
  4. 加入社区:和其他小程序员交流,分享作品

记住

学习编程不是为了成为程序员,而是为了理解数字世界,培养逻辑思维,享受创造的乐趣!

祝你在编程的世界里继续探索,创造更多精彩的作品!🎉


🎓 毕业快乐!你已经完成了Python基础教程的所有学习!

reference