while 循环与循环嵌套
本文档带你掌握条件循环与循环嵌套,从简单重复到复杂模式,让代码更有威力!
📋 内容导航
-
内容回顾 - 温习for循环与列表字符串
-
[while 循环](#while 循环) - 条件循环的语法与应用
-
循环嵌套 - 多层循环的执行逻辑
-
综合实战 - 输入验证与异常处理
-
通关挑战 - 练习题与详细解析
-
文档总结 - 知识点回顾与拓展
🎯 内容回顾
上一篇我们学会了 for 循环,它能帮我们轻松搞定"重复N次"的任务。但现实往往没那么理想 ------ 很多时候我们根本不知道要重复多少次!
想象一下这些场景:
🎮 猜数字游戏
-
玩家:"我猜50!"
-
系统:"大了"
-
玩家:"那25!"
-
系统:"小了"
-
...(不知道要猜几次才能中)
💬 聊天机器人
-
用户说一句,机器人回一句
-
直到用户说"再见"才结束
-
(不知道要聊多少轮)
🎰 抽奖系统
-
"再抽一次!"
-
"再抽一次!"
-
直到中奖或金币花光
-
(完全看运气)
这些场景的共同点是:不知道什么时候结束,只知道结束的条件。
这就需要本文的主角 ------ while 循环(条件循环)!
本文学习目标
| 目标 | 内容 | 实战项目 |
|---|---|---|
| while 循环 | 条件为真就一直执行 | 改进猜数字游戏 |
| 循环嵌套 | 多层循环的执行逻辑 | 打印九九乘法表 |
| 输入验证 | 处理各种用户输入 | 健壮的猜数字游戏 |
while 循环
什么是 while 循环?
while 循环的中文意思是"当...时",它的逻辑很简单:
当条件为真,就重复执行代码块;条件为假,就结束循环。
这就像是:"只要还没吃饱,就继续吃" ------ 你不知道要吃多少口,只知道什么时候该停。
while vs if 的区别
| 语句 | 条件为真时 | 执行次数 |
|---|---|---|
if |
执行一次 | 0次或1次 |
while |
执行完再判断,反复执行 | 0次、1次或多次 |
记忆口诀 :if 是"看一眼就走",while 是"不达目的不罢休"!
while 语法
while 条件表达式:
循环体
else:
条件为False时执行(可选)
示例:1到100求和
total = 0
addend = 1
while addend <= 100:
total += addend
addend += 1
print('1到100各数相加的和 = ', total) # 5050
对比 for 循环:
# for 循环版本
total = 0
for i in range(1, 101):
total += i
print(total)
什么时候用 while?
-
循环次数不确定,但有明确的结束条件
-
需要一直等待某个条件发生
示例:水仙花数
水仙花数:一个三位数,其各位数字立方和等于该数本身。
# while 版本
num = 100
while num <= 999:
i = num // 100 # 百位
j = num // 10 % 10 # 十位
k = num % 10 # 个位
if i**3 + j**3 + k**3 == num:
print(num)
num += 1
# for 版本
for n in range(100, 1000):
i, j, k = n // 100, n // 10 % 10, n % 10
if i**3 + j**3 + k**3 == n:
print(n)
实战:改进猜数字游戏
import random
secret = random.randint(0, 100)
print("猜一个0-100之间的数字")
guess = int(input("你的猜测:"))
while guess != secret:
if guess > secret:
print("猜大了")
else:
print("猜小了")
guess = int(input("再猜一次:"))
print("恭喜猜对了!")
无限循环
while True: # 永远为真,无限循环
msg = input("说点啥(输入quit退出):")
if msg == "quit":
break # 用break跳出循环
print(f"你说了:{msg}")
什么时候用无限循环?
-
服务器程序(7×24小时运行)
-
游戏主循环
-
需要一直等待用户输入
循环嵌套
循环嵌套就是在一个循环体中嵌入另一个循环。
执行流程:
-
外层循环执行一次,内层循环执行完整的一轮
-
外层循环再执行一次,内层循环再执行完整的一轮
-
以此类推
打印九九乘法表
# 基础版本(有重复)
for i in range(1, 10):
for j in range(1, 10):
print(f'{j}*{i}={i*j}', end='\t')
print()
# 优化版本(去重复)
for i in range(1, 10):
for j in range(1, i + 1): # 关键:j的范围是1到i
print(f'{j}*{i}={i*j}', end='\t')
print()
输出:
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
...
格式化对齐
让输出更整齐:
for i in range(1, 10):
for j in range(1, i + 1):
print(f'{j}*{i}={i*j:2}', end=' ') # :2 表示占2位宽度
print()
字符串格式化速查
| 格式 | 含义 | 示例 |
|---|---|---|
{:2} |
宽度为2 | 5 → 5 |
{:>2} |
右对齐 | 5 → 5 |
{:<2} |
左对齐 | 5 → 5 |
{:0>2} |
左边补0 | 5 → 05 |
{:.2f} |
保留2位小数 | 3.14159 → 3.14 |
综合实战:健壮的猜数字游戏
import random
def get_valid_number(prompt):
"""获取有效的数字输入"""
while True:
user_input = input(prompt)
if user_input.isdigit():
return int(user_input)
print("请输入数字!")
# 游戏主程序
secret = random.randint(0, 100)
print("猜一个0-100之间的数字")
attempts = 0
while True:
guess = get_valid_number("你的猜测:")
attempts += 1
if guess == secret:
print(f"恭喜猜对了!用了{attempts}次")
break
elif guess > secret:
print("猜大了")
else:
print("猜小了")
🎉 文档总结
一、核心知识点回顾
1. while 循环
| 特性 | 说明 | 示例 |
|---|---|---|
| 语法 | while 条件: |
while i <= 100: |
| 执行逻辑 | 条件为真就执行,执行完再判断 | 可能执行0次、1次或多次 |
| 适用场景 | 不知道循环次数,只知道结束条件 | 猜数字、用户输入验证 |
| else子句 | 循环正常结束时执行(非break退出) | while...else... |
for vs while 选择指南:
┌─────────────────────────────────────────┐
│ 需要循环吗? │
│ ├── 知道循环次数? │
│ │ └── 是 → 用 for │
│ │ for i in range(10): │
│ │ │
│ └── 不知道次数,只知道条件? │
│ └── 是 → 用 while │
│ while 条件: │
└─────────────────────────────────────────┘
2. 循环嵌套
执行规则:外层循环执行1次,内层循环执行完整的一轮
for i in range(3): # 外层循环3次
for j in range(3): # 内层循环3次
print(i, j)
# 总共执行 3 × 3 = 9 次
常见嵌套模式:
-
九九乘法表:
i控制行,j控制列 -
图形打印:外层控制行,内层控制每行的内容
-
排列组合:多层循环生成所有可能
3. 无限循环与 break
while True: # 无限循环
代码块
if 退出条件:
break # 跳出循环
使用场景:
-
游戏主循环
-
服务器程序
-
需要一直等待用户输入
4. 字符串格式化
| 格式 | 含义 | 示例 | 输出 |
|---|---|---|---|
{:2} |
宽度2,右对齐 | f"{5:2}" |
" 5" |
{:<2} |
宽度2,左对齐 | f"{5:<2}" |
"5 " |
{:0>2} |
宽度2,左边补0 | f"{5:0>2}" |
"05" |
{:.2f} |
保留2位小数 | f"{3.14159:.2f}" |
"3.14" |
二、编程思维培养
1. 条件循环思维
核心思想:不关注"做几次",关注"什么时候停"
实际应用:
-
登录系统:一直提示输入密码,直到正确
-
文件读取:一直读取,直到文件末尾
-
网络请求:一直重试,直到成功或超过最大次数
2. 嵌套分解思维
核心思想:复杂问题分解为多个简单层次
以九九乘法表为例:
问题:打印九九乘法表
├── 第一层:控制行数(1-9行)
│ └── for i in range(1, 10)
├── 第二层:控制每行的列数(第i行有i列)
│ └── for j in range(1, i+1)
└── 第三层:打印每个算式
└── print(f"{j}*{i}={i*j}", end='\t')
3. 模式识别思维
核心思想:观察规律,用数学表达式描述
图形打印通用套路:
-
观察每行的变化规律(空格数、符号数)
-
用行号
i表示规律 -
用循环生成每行内容
三、常见错误与避坑指南
❌ 错误1:死循环(忘记更新条件变量)
# 错误
i = 1
while i <= 10:
print(i)
# 忘记写 i += 1,导致无限循环
# 正确
i = 1
while i <= 10:
print(i)
i += 1 # 记得更新条件变量!
❌ 错误2:循环条件永远为假
# 错误
i = 100
while i < 10: # 条件一开始就为假,循环体一次都不执行
print(i)
i += 1
# 检查:确保初始值满足循环条件
❌ 错误3:混淆 break 和 continue
# break:跳出整个循环
for i in range(10):
if i == 5:
break # 直接结束循环,不再执行后续迭代
print(i) # 输出 0,1,2,3,4
# continue:跳过本次循环的剩余代码
for i in range(10):
if i == 5:
continue # 跳过i=5的print,继续下一次迭代
print(i) # 输出 0,1,2,3,4,6,7,8,9
❌ 错误4:嵌套循环的缩进错误
# 错误:内层循环没有正确缩进
for i in range(3):
for j in range(3): # IndentationError!
print(i, j)
# 正确
for i in range(3):
for j in range(3): # 注意缩进!
print(i, j)
❌ 错误5:字符串格式化语法错误
# 错误
print("{:2}".format(5)) # 正确,但f-string更简洁
print(f"{5:2d}") # 正确
print(f"{5:2f}") # 错误!整数不能用f格式
# 正确
print(f"{5:2}") # 整数默认格式
print(f"{5.5:2f}") # 浮点数用f格式
四、知识延伸与拓展
1. 循环中的 else 子句
while 和 for 都可以有 else,在循环正常结束 时执行(不是被 break 打断):
for i in range(5):
if i == 10: # 不会执行
break
else:
print("循环正常结束,没有被break") # 会执行
for i in range(5):
if i == 3:
break
else:
print("循环正常结束") # 不会执行,因为被break了
实用场景:查找元素,找不到时执行else
target = 7
for i in range(5):
if i == target:
print("找到了!")
break
else:
print("没找到") # target=7不在range(5)中,会执行
2. 迭代器与生成器(进阶)
range() 返回的是一个可迭代对象,不是列表(Python 3),节省内存:
# range(1000000) 不会创建包含100万个数的列表
# 而是创建一个"按需生成"的迭代器
# 如果需要列表,可以转换
numbers = list(range(10)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3. 列表推导式(简化循环)
用一行代码替代简单的for循环:
# 传统写法
squares = []
for i in range(10):
squares.append(i ** 2)
# 列表推导式(更简洁)
squares = [i ** 2 for i in range(10)]
# 带条件的列表推导式
evens = [i for i in range(10) if i % 2 == 0] # [0, 2, 4, 6, 8]
4. enumerate() 函数
同时获取索引和元素:
fruits = ['苹果', '香蕉', '橙子']
# 传统写法
for i in range(len(fruits)):
print(f"{i}: {fruits[i]}")
# 用enumerate更优雅
for i, fruit in enumerate(fruits):
print(f"{i}: {fruit}")
# 输出:
# 0: 苹果
# 1: 香蕉
# 2: 橙子
五、下篇预告
在下一篇文档中,我们将学习:
函数(Function)
-
什么是函数?为什么要用函数?
-
函数的定义与调用
-
参数与返回值
-
变量的作用域(局部变量 vs 全局变量)
-
默认参数、可变参数、关键字参数
-
递归函数(函数调用自身)
-
lambda 表达式(匿名函数)
-
内置函数速查
学习目标:
-
掌握"代码复用"的核心思想
-
学会将复杂问题分解为多个函数
-
能够编写结构清晰、可维护的代码
实战项目:
-
编写一个计算器程序(加减乘除函数)
-
实现一个简易的通讯录管理系统
💡 学习建议:
循环是编程的基础,一定要多练习图形打印题(金字塔、菱形等)
理解嵌套循环的执行流程,可以画图辅助理解
遇到死循环不要慌,按
Ctrl+C可以强制终止字符串格式化很实用,建议熟记常用的几种格式
🏆 通关挑战
选择题
1. 以下代码有什么问题?
tag = 'y'
for tag == 'y':
print('好好学习')
tag = input('继续?y/n:')
-
A. 第1行改成
tag=y -
B. 第2行
for改成while -
C. 第3行改成
print("我要好好学习") -
D. 没有错误
2. 想要输出10行10列的*,代码对吗?
for i in range(1, 10):
for j in range(1, 11):
print('*', end='')
print()
-
A. 第1行10改成11
-
B. 第2行11改成10
-
C. 第2行 range 改成 random
-
D. 正确
🔑 答案与解析:
第1题:答案 B
解析过程:
-
首先看代码结构,第2行用的是
for tag == 'y',这是错误的语法 -
for循环后面应该跟可迭代对象(如range()、列表、字符串等),而不是条件表达式 -
这里需要实现的是"条件为真就继续循环"的逻辑,应该用
while循环 -
正确写法:
while tag == 'y': -
其他选项分析:
-
A选项:改成
tag=y会把y当成变量,但y未定义,会报错 -
C选项:引号问题不是语法错误的关键
-
D选项:明显有语法错误
-
第2题:答案 A
解析过程:
-
题目要求输出10行10列,即外层循环10次,内层循环10次
-
range(1, 10)生成的是 1-9,只有9个数,所以只会输出9行 -
要输出10行,需要改成
range(1, 11)或range(10) -
其他选项分析:
-
B选项:内层循环11改成10,只会输出9列,不符合要求
-
C选项:
random是随机模块,不能替代range -
D选项:代码有错误,不能正确输出10行
-
编程题
1. 求10-300以内同时能被7和8整除的整数。
🔑 参考实现与解析:
点击查看代码
# 方法1:遍历判断(通用方法)
for num in range(10, 301):
if num % 7 == 0 and num % 8 == 0:
print(num)
# 方法2:数学优化(7和8的最小公倍数是56)
for num in range(56, 301, 56):
if num >= 10:
print(num)
</details>
思路解析:
-
理解题意:"同时能被7和8整除"意味着这个数是7和8的公倍数
-
方法1 :遍历10-300的每个数,用
%判断是否整除,逻辑简单直接 -
方法2:利用数学知识,7和8互质,最小公倍数是 7×8=56,所以只需遍历56的倍数
-
效率对比:方法2只需遍历5次(56, 112, 168, 224, 280),方法1需要遍历291次
扩展思考:
-
如果求能被3、5、7同时整除的数呢?(提示:最小公倍数是105)
-
如何用
while循环实现这道题?
2. 打印1-100以内含有6的数。
🔑 参考实现与解析:
点击查看代码
# 方法1:字符串判断(简洁)
for num in range(1, 101):
if '6' in str(num):
print(num)
# 方法2:数学方法(不用字符串转换)
for num in range(1, 101):
temp = num
while temp > 0:
if temp % 10 == 6: # 个位是6
print(num)
break
temp //= 10 # 去掉个位
</details>
思路解析:
-
方法1 :将数字转为字符串,用
'6' in str(num)判断是否包含字符'6'-
优点:代码简洁,易读
-
缺点:涉及类型转换,效率略低
-
-
方法2:用数学方法逐位检查
-
temp % 10获取个位数 -
temp //= 10去掉个位数(整除10) -
优点:纯数学运算,效率更高
-
缺点:代码较复杂
-
扩展思考:
-
如何打印不含6的数?(提示:
if '6' not in str(num)) -
如何统计含有6的数的个数?
3. 用1、2、3、4能组成多少个互不相同且无重复数字的三位数?各是多少?
🔑 参考实现与解析:
点击查看代码
count = 0
for i in [1, 2, 3, 4]: # 百位
for j in [1, 2, 3, 4]: # 十位
for k in [1, 2, 3, 4]: # 个位
if i != j and j != k and i != k: # 三个数字互不相同
print(f"{i}{j}{k}")
count += 1
print(f"共{count}个")
</details>
思路解析:
-
问题分析:三位数有百位、十位、个位,每个位置可以是1、2、3、4,但不能重复
-
算法思路:三层循环嵌套,分别代表三个位置
-
外层循环
i:百位,有4种选择 -
中层循环
j:十位,有4种选择 -
内层循环
k:个位,有4种选择
-
-
去重逻辑 :
if i != j and j != k and i != k确保三个数字互不相同 -
数学验证:根据排列组合,应该是 4×3×2 = 24 个
执行流程示例:
-
i=1, j=2, k=3 → 123 ✓
-
i=1, j=2, k=4 → 124 ✓
-
i=1, j=2, k=2 → 跳过(j==k,重复)
扩展思考:
-
如果数字可以重复,有多少个?(提示:4×4×4=64)
-
如果是四位数呢?(提示:需要四层循环)
4. 打印金字塔(输入n=5)。
*
***
*****
*******
*********
🔑 参考实现与解析:
点击查看代码
n = int(input("输入层数:"))
for i in range(1, n + 1):
spaces = ' ' * (n - i) # 空格数:n-i
stars = '*' * (2 * i - 1) # 星号数:2*i-1
print(spaces + stars)
</details>
思路解析:
-
观察规律:以n=5为例
-
第1行:4个空格 + 1个星号
-
第2行:3个空格 + 3个星号
-
第3行:2个空格 + 5个星号
-
...
-
第i行:(n-i)个空格 + (2*i-1)个星号
-
-
核心技巧 :字符串乘法
' ' * 4生成" "(4个空格) -
对齐原理:先打印空格让星号右移,再打印星号
数学规律表:
| 行号i | 空格数(n-i) | 星号数(2i-1) | 效果 |
|---|---|---|---|
| 1 | 4 | 1 | * |
| 2 | 3 | 3 | *** |
| 3 | 2 | 5 | ***** |
| 4 | 1 | 7 | ******* |
| 5 | 0 | 9 | ********* |
扩展思考:
-
如何打印空心金字塔?(只有边框有星号)
-
如何打印菱形(金字塔+倒金字塔)?
5. 打印直角三角形(输入n=4)。
*
**
***
****
🔑 参考实现与解析:
点击查看代码
n = int(input("输入层数:"))
for i in range(1, n + 1):
print('*' * i)
</details>
思路解析:
-
观察规律:第i行打印i个星号
-
第1行:1个星号
-
第2行:2个星号
-
第3行:3个星号
-
...
-
-
实现技巧 :
'\*' \* i生成i个星号组成的字符串 -
简化写法:不需要内层循环,直接用字符串乘法
对比学习:
-
直角三角形:每行星号数递增(1, 2, 3, 4...)
-
金字塔:每行星号数也是递增,但前面要加空格对齐(1, 3, 5, 7...)
扩展思考:
-
如何打印左对齐的直角三角形?(已经是左对齐了)
-
如何打印右对齐的直角三角形?(提示:先打印空格)
6. 打印数字金字塔(输入n=4)。
1
121
12321
1234321
🔑 参考实现与解析:
点击查看代码
n = int(input("输入层数:"))
for i in range(1, n + 1):
# 打印空格
print(' ' * (n - i), end='')
# 打印递增部分:1, 12, 123, 1234
for j in range(1, i + 1):
print(j, end='')
# 打印递减部分:无, 1, 21, 321
for j in range(i - 1, 0, -1):
print(j, end='')
print()
</details>
思路解析:
-
结构分析:每行分为三部分
-
空格部分:右对齐用,数量为
n-i -
递增部分:从1到i,如
1,12,123,1234 -
递减部分:从i-1到1,如 ```,``1
,21,321`
-
-
关键技巧 :
range(i - 1, 0, -1)生成递减序列-
start=i-1:从i-1开始 -
stop=0:到0结束(不包含0) -
step=-1:每次减1
-
-
拼接效果:递增 + 递减 = 对称数字
执行流程(n=3时):
-
i=1:空格" " + 递增"1" + 递减"" → " 1"
-
i=2:空格" " + 递增"12" + 递减"1" → " 121"
-
i=3:空格"" + 递增"123" + 递减"21" → "12321"
扩展思考:
-
如何打印字母金字塔?如
A,ABA,ABCBA -
如何打印乘法金字塔?如
1,1*2*1,1*2*3*2*1