三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Python while循环嵌套:从基础语法到实战应用与避坑指南

Python while循环嵌套:从基础语法到实战应用与避坑指南

1. 从“单打独斗”到“协同作战”:理解循环嵌套的本质

很多刚开始接触Python的朋友,在掌握了whilefor这两个基本循环后,往往会觉得编程已经入门了。直到有一天,你遇到了一个需求:要打印一个由星号组成的5行5列的矩形,或者要处理一个像Excel表格那样的二维数据。你可能会想,一个循环走一遍不就行了吗?结果写出来发现,要么只能打印一行,要么逻辑一团糟。这个时候,你就需要请出编程中的“阵法大师”——循环嵌套

简单来说,循环嵌套就是在一个循环的内部,再完整地放入另一个循环。你可以把它想象成现实生活中的“双层工作流程”。比如,工厂里生产一批玩具车,外层循环是“处理每一辆玩具车”,而内层循环则是“为这辆玩具车安装每一个轮子”。你必须为每一辆车(外层循环的一次迭代),都完整地执行一遍“安装四个轮子”(内层循环的完整执行)的过程。

在Python中,while循环的嵌套,就是利用while这种“只要条件为真,就反复执行”的特性,构建出这种多层次、结构化的重复逻辑。它特别适合处理那些次数不固定、需要根据运行时条件动态决定的内外层循环场景。与for循环嵌套常用于遍历已知序列(如列表、元组)不同,while循环嵌套在处理未知循环次数、交互式程序或等待特定状态变化时,更具灵活性。

2. 核心概念拆解:当while遇见while

2.1 基础语法结构与执行流程

一个标准的while循环嵌套结构看起来是这样的:

# 外层循环初始化 outer_condition = True while outer_condition: # 外层循环体代码... # 内层循环初始化 inner_condition = True while inner_condition: # 内层循环体代码... # 更新内层循环条件,否则会死循环 inner_condition = update_inner_condition() # 外层循环体其他代码... # 更新外层循环条件 outer_condition = update_outer_condition()

它的执行流程是由外向内,层层推进的:

  1. 程序首先判断外层循环的条件outer_condition,若为True,则进入外层循环体。
  2. 执行外层循环体内,在遇到内层while语句前的代码。
  3. 遇到内层while后,开始判断内层循环条件inner_condition
  4. 只要inner_conditionTrue,就会完整地执行内层循环体,直到inner_condition变为False,内层循环结束。
  5. 内层循环结束后,程序继续执行外层循环体内,内层循环之后的剩余代码。
  6. 完成本次外层循环体的所有执行后,程序跳回到外层while语句,再次判断outer_condition
  7. 如果outer_condition仍为True,则重复步骤2-6;如果变为False,则整个嵌套循环结束。

这里有一个非常关键的理解点:对于外层循环的每一次迭代,内层循环都会从头到尾、完整地执行一遍。内层循环就像是外层循环的“子任务”,每次外层循环开始新一轮,这个子任务都要被重新初始化并执行。

2.2 一个简单的入门示例:打印矩形

让我们用打印一个5行5列的星号矩形这个经典例子,来直观感受一下:

row = 1 # 初始化外层循环计数器,控制行数 while row <= 5: # 外层循环:控制打印多少行 col = 1 # 初始化内层循环计数器,控制列数。注意:每次外层循环开始,col都被重置为1! while col <= 5: # 内层循环:控制当前行打印多少列(多少个星号) print('*', end=' ') # 打印一个星号,用空格结尾不换行 col += 1 # 内层循环计数器加1,准备打印下一个星号 print() # 一行打印完毕,输出一个换行,切换到下一行 row += 1 # 外层循环计数器加1,准备打印下一行

执行过程拆解:

  • row = 1时,进入外层循环。
  • col被重置为1。开始内层循环:col从1到5,连续打印5个*,中间用空格隔开。
  • 内层循环结束(col=6,条件col<=5为假),执行print()换行。
  • row加1变为2。
  • 回到外层循环判断,row=2<=5为真,开始第二轮。关键来了col再次被重置为1,然后内层循环再次完整地从1执行到5,打印第二行的5个星号。
  • 如此反复,直到row变为6,外层循环结束。

注意:内层循环的初始化(col = 1)必须放在外层循环体内部、内层while之前。如果错误地放在外层循环之前,那么col只会在最开始初始化一次。当打印完第一行后,col已经变成6,第二行开始内层循环条件col <= 5直接为假,导致第二行无法打印任何星号。这是新手最常见的错误之一。

2.3 与for循环嵌套的对比与选型

你可能会问,这个例子用for循环写起来不是更简洁吗?确实如此:

for i in range(5): for j in range(5): print('*', end=' ') print()

那么,什么时候该用while循环嵌套,而不是for呢?核心区别在于循环的驱动方式

  • for循环嵌套:驱动循环的是“一个可迭代对象”。你明确知道要遍历一个序列(如range(5),一个列表,一个文件的所有行),循环次数在开始时就基本确定了。它的逻辑是“对每一个元素做某事”。
  • while循环嵌套:驱动循环的是“一个条件”。你不知道具体要循环多少次,只知道“当某个条件满足时,就要继续循环”。它的逻辑是“只要情况还是这样,就继续做某事”。

因此,选型原则如下:

  • for循环嵌套:当你需要遍历已知结构的数据时,例如处理二维列表、遍历文件行和列、生成固定模式的图形等。代码更简洁,不易出错。
  • while循环嵌套
    1. 循环次数未知:例如,读取用户输入直到他输入“quit”,并且对每次输入的内容进行逐字符分析(外层while控制是否继续读取,内层whilefor遍历字符串)。
    2. 等待状态变化:例如,在简单游戏循环中,外层while判断游戏是否结束,内层while处理本回合内的连续操作。
    3. 处理非序列化的动态条件:例如,监控一个队列,外层while判断队列是否为空,内层while处理当前队首元素的多个关联任务。

3. 核心应用场景与实战解析

理解了基本概念后,我们来看几个while循环嵌套的典型应用场景。这些场景能更好地体现它相对于for循环的优势。

3.1 场景一:交互式菜单与子任务处理

这是控制台应用程序的常见模式。程序展示一个主菜单,用户选择后进入相应的子功能,子功能本身可能又是一个需要循环处理的任务。

def handle_subtask_1(): """处理子任务1:连续数字求和""" total = 0 print("进入连续求和模式,输入非数字结束。") while True: user_input = input("请输入一个数字: ") if not user_input.isdigit(): # 退出条件:输入的不是纯数字 break total += int(user_input) print(f"当前累计和: {total}") print(f"子任务1结束,最终和为: {total}\n") def handle_subtask_2(): """处理子任务2:猜数字游戏""" import random target = random.randint(1, 100) attempts = 0 print("猜数字游戏开始!范围1-100。") while True: guess_input = input("请输入你的猜测: ") if not guess_input.isdigit(): print("请输入有效数字!") continue guess = int(guess_input) attempts += 1 if guess < target: print("猜小了!") elif guess > target: print("猜大了!") else: print(f"恭喜你!猜对了!用了{attempts}次。\n") break # 主程序 - 使用while循环嵌套的核心逻辑 program_running = True while program_running: # 外层循环:控制整个程序是否继续运行 print("="*20) print("1. 连续数字求和") print("2. 猜数字游戏") print("3. 退出程序") print("="*20) choice = input("请选择功能 (1/2/3): ") if choice == '1': handle_subtask_1() # 子任务内部有自己的while循环 elif choice == '2': handle_subtask_2() # 子任务内部有自己的while循环 elif choice == '3': program_running = False # 改变外层循环条件,退出主循环 print("程序退出,感谢使用!") else: print("无效选择,请重新输入!")

代码解析与心得:

  • 外层循环 (while program_running): 条件很简单,就是一个布尔标志。它负责维持程序的“生命”,只要用户不选择退出,程序就永远展示主菜单。
  • 内层循环 (子任务中的while True): 每个子任务函数内部都有自己的循环,负责处理一个独立、连续的业务流程。例如,handle_subtask_1中的循环持续读取数字直到遇到非数字输入;handle_subtask_2中的循环持续进行猜数字直到猜中。
  • 嵌套关系: 这不是严格的语法嵌套(一个while套在另一个while的代码块里),而是逻辑上的嵌套。外层循环管理着应用程序的“状态”(运行/退出),而内层循环(子任务)是在外层循环的某一次迭代中被调用的“子状态机”。这种模式非常清晰地将不同层级的控制逻辑分离开。
  • 注意事项: 务必确保每个内层循环都有明确的退出条件(如break或改变循环条件),否则一旦进入某个子任务就再也回不到主菜单了。同时,子任务结束后要清晰地返回到外层循环的上下文,这里是靠函数返回来自然实现的。

3.2 场景二:动态数据集的逐层处理

假设我们从一个网络接口或传感器持续读取数据包,每个数据包内部又包含多个需要独立处理的数据块。数据包的到达是间歇性的,每个数据包内数据块的数量也不固定。

import random import time def simulate_packet_arrival(): """模拟随机时间到达的数据包,包内包含随机数量的数据块""" # 随机决定是否有一个新数据包到达 if random.random() > 0.7: # 30%的几率有数据包 packet_id = random.randint(1000, 9999) num_blocks = random.randint(1, 5) # 每个包有1-5个数据块 blocks = [random.randint(10, 99) for _ in range(num_blocks)] return {'id': packet_id, 'blocks': blocks} return None # 模拟处理程序 timeout = 10 # 运行10秒 start_time = time.time() last_packet_time = start_time print("开始监听数据包...") while time.time() - start_time < timeout: # 外层循环:条件是基于时间的 # 检查是否有新数据包到达 packet = simulate_packet_arrival() if packet: last_packet_time = time.time() print(f"\n[{time.time()-start_time:.1f}s] 收到数据包 ID: {packet['id']}") block_index = 0 # 内层循环:处理当前数据包内的所有数据块 while block_index < len(packet['blocks']): data = packet['blocks'][block_index] # 模拟处理一个数据块需要一点时间 time.sleep(0.2) # 模拟处理逻辑,这里只是简单打印 print(f" 处理数据块 {block_index+1}: 数值 {data}, 结果 {data*2}") block_index += 1 print(f"数据包 {packet['id']} 处理完毕。") else: # 没有数据包时,可以做一些其他工作,比如检查超时 if time.time() - last_packet_time > 3.0: # 3秒无数据包 print(f"[{time.time()-start_time:.1f}s] 警告:数据流中断超过3秒。") time.sleep(0.5) # 短暂休眠,避免CPU空转 print(f"\n运行时间到,共运行 {timeout} 秒。")

代码解析与心得:

  • 外层循环 (while time.time() - start_time < timeout): 这是一个典型的基于时间的循环条件。程序需要运行一段固定的时长,或者直到某个超时事件发生。这用for循环很难直观表达,用while则非常自然。
  • 内层循环 (while block_index < len(packet['blocks'])): 这是基于数据长度的循环。虽然这里用for block in packet['blocks']:会更简洁,但使用while可以更灵活地控制索引。例如,如果在处理某个数据块时发生错误,可能需要根据错误类型决定是重试当前块(block_index不变)、跳过当前块(block_index += 1)还是跳过后续所有块(break),这种动态控制用while配合索引更容易实现。
  • 循环条件的动态性: 这个例子展示了嵌套循环中,内外层条件可以完全不同且动态变化。外层是时间驱动,内层是数据驱动。这种组合在处理实时流数据、事件监听等场景非常有用。
  • 注意事项: 在这种包含time.sleep()或等待I/O的循环中,要特别注意循环条件的更新是否及时。另外,内层循环如果处理时间过长,可能会影响外层循环对其他事件(如超时检查)的响应速度。在真实场景中,可能需要使用多线程或异步IO来处理。

3.3 场景三:算法实现——冒泡排序

许多基础算法天然适合用嵌套循环来实现。冒泡排序就是一个经典例子,它通过反复交换相邻的逆序元素来将列表排序。

def bubble_sort_while(arr): """ 使用while循环嵌套实现冒泡排序。 直观体现了‘未排序区间’逐渐缩小的过程。 """ n = len(arr) # 外层循环:控制排序的‘趟数’。最多需要n-1趟。 i = 0 while i < n - 1: swapped = False # 标志位,记录本轮是否发生交换 # 内层循环:进行相邻元素的比较和交换,完成一趟‘冒泡’ j = 0 while j < n - 1 - i: # 关键:已排序好的部分不需要再比较 if arr[j] > arr[j + 1]: # 交换元素 arr[j], arr[j + 1] = arr[j + 1], arr[j] swapped = True j += 1 print(f"第{i+1}趟排序后: {arr} (比较范围: 0 到 {n-2-i})") # 优化:如果本轮没有发生任何交换,说明列表已经有序,提前结束 if not swapped: print(f"第{i+1}趟后已无交换,排序提前完成。") break i += 1 return arr # 测试 test_list = [64, 34, 25, 12, 22, 11, 90] print("原始列表:", test_list) sorted_list = bubble_sort_while(test_list) print("最终排序结果:", sorted_list)

代码解析与心得:

  • 外层循环 (while i < n - 1): 变量i可以理解为“已经完成排序的尾部元素数量”,或者“已经执行的趟数”。每一趟排序都会将当前未排序部分的最大值“冒泡”到正确位置。
  • 内层循环 (while j < n - 1 - i): 这是算法的核心。变量j是当前比较的索引。条件j < n - 1 - i是精髓,因为经过i趟后,列表末尾的i个元素已经是最大的且排好序的了,无需再参与比较。这体现了循环嵌套中,内层循环的边界条件依赖于外层循环的变量,这是嵌套循环强大能力的体现。
  • 优化技巧 (swapped标志): 这是一个实用的优化。如果某一趟内层循环没有发生任何交换,意味着整个列表已经有序,可以立即终止外层循环,避免无谓的后续遍历。这种“提前退出”的逻辑在嵌套循环中很常见,能有效提升效率。
  • 与for循环实现的对比: 用for循环实现冒泡排序同样清晰:
    for i in range(n-1): for j in range(0, n-1-i): if arr[j] > arr[j+1]: arr[j], arr[j+1] = arr[j+1], arr[j]
    while循环版本的优势在于可以更灵活地控制循环变量。例如,如果想实现一个“双向冒泡排序”(鸡尾酒排序),即奇数趟从左到右冒泡,偶数趟从右到左冒泡,用while循环来控制方向切换会更直观。而for循环版本则显得更为简洁和“Pythonic”,在已知明确迭代次数时是首选。

4. 避坑指南与性能优化

嵌套循环,尤其是while循环嵌套,如果使用不当,很容易掉进坑里。下面总结几个常见问题和优化思路。

4.1 常见陷阱与调试技巧

  1. 死循环(无限循环)

    • 问题: 这是while循环嵌套最危险的陷阱。如果内层或外层循环的条件永远无法变为False,程序就会卡死。
    • 原因
      • 忘记在循环体内更新循环条件变量(如i += 1)。
      • 循环条件逻辑错误,例如while flag == True,但flag从未被设置为False
      • 内层循环的退出条件依赖于外层循环的某个状态,但这个状态在内层循环中永远不会被改变。
    • 调试
      • 添加打印语句:在内外层循环开始时打印关键变量的值。这是最直接有效的方法。
        outer_count = 0 while outer_condition: outer_count += 1 print(f"外层循环第{outer_count}次,条件变量={outer_condition}") inner_count = 0 while inner_condition: inner_count += 1 print(f" 内层循环第{inner_count}次,条件变量={inner_condition}") # ... 更新 inner_condition # ... 更新 outer_condition
      • 设置安全计数器:对于理论上应有上限的循环,添加一个“保险丝”。
        max_outer_iterations = 1000 outer_iter = 0 while outer_condition and outer_iter < max_outer_iterations: outer_iter += 1 # ... 循环体 if outer_iter == max_outer_iterations: print("警告:外层循环可能进入死循环,已强制终止。")
  2. 内层循环变量初始化位置错误

    • 问题: 如2.2节示例所述,内层循环的计数器或状态变量初始化必须放在外层循环体内、内层循环开始前。
    • 错误示例
      i = 0 j = 0 # 错误!j在这里初始化 while i < 3: while j < 2: print(i, j) j += 1 i += 1 # 输出: (0,0), (0,1) 然后就结束了。因为j在第一次外层循环后变成了2,再也进不去内层循环。
    • 修正: 将j = 0移到第一个while循环的内部。
  3. 复杂的条件逻辑导致难以理解或维护

    • 问题: 在循环条件中写入过于复杂的布尔表达式,或者循环体内有多个breakcontinue,使得程序流程难以追踪。
    • 建议
      • 将复杂的条件判断提取成布尔变量或函数,让while语句本身保持简洁。
        # 不易读 while (user_input != 'quit' and not is_timeout) or (force_continue and retry_count < 3): # ... # 改进后 should_continue = (user_input != 'quit' and not is_timeout) or (force_continue and retry_count < 3) while should_continue: # ... 在循环体内适时更新 should_continue 或它的组成部分
      • 尽量避免在深层嵌套的循环中使用break来跳出多层。考虑使用“标志变量”或将内层循环封装成函数,用return来代替。

4.2 性能考量与优化策略

嵌套循环的时间复杂度通常是O(n²)或更高,是性能瓶颈的常见来源。

  1. 减少不必要的迭代

    • 尽早break: 一旦找到所需结果或满足条件,立即跳出内层甚至外层循环。
    • 缩小内层循环范围: 像冒泡排序例子中j < n - 1 - i那样,随着外层循环进行,动态减少内层循环的工作量。
    • 跳过无效迭代: 在内层循环开始前,通过if判断是否可以跳过本次外层迭代的整个内层处理。
  2. 将条件判断外提

    • 如果内层循环的某些判断条件在外层循环的一次迭代中是不变的,就把它提到内层循环之外计算一次。
    • 低效示例
      while i < len(data): while j < len(processed_data): if complex_function(data[i]): # 这个判断结果对同一个i是固定的 # 做一些操作 j += 1 i += 1
    • 优化后
      while i < len(data): condition_result = complex_function(data[i]) # 提到外层计算一次 while j < len(processed_data): if condition_result: # 做一些操作 j += 1 i += 1
  3. 考虑使用更高效的数据结构或算法

    • 这是根本性的优化。例如,查找操作中,嵌套循环的线性查找是O(n²),而使用字典(哈希表)进行查找可以降到平均O(n)。
    • 对于数值计算或数据处理,考虑使用NumPy等库的向量化操作,它用底层C语言实现的循环替代了Python层的嵌套循环,性能有数量级的提升。
  4. 重构代码

    • 如果嵌套层数过深(比如超过3层),代码很可能变得难以理解和维护。考虑是否可以通过以下方式重构:
      • 将内层循环提取为独立函数:使主逻辑更清晰。
      • 使用列表推导式(对于简单转换):虽然可能还是隐含嵌套,但语法更简洁,且解释器对其有一定优化。
      • 审视业务逻辑:深嵌套有时意味着设计可以简化。是否有不必要的循环?数据是否可以预先组织成更方便查找的结构?

5. 进阶模式与替代方案

当你熟练掌握了基本的while循环嵌套后,可以探索一些更高级的模式,并了解在哪些情况下有更好的替代方案。

5.1 使用标志变量控制多层循环

有时你需要从深层嵌套的内层循环中,直接跳出到最外层。单独使用break只能跳出当前一层循环。这时可以使用一个“标志变量”。

found = False # 标志变量 outer_idx = 0 data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] target = 5 while outer_idx < len(data) and not found: inner_list = data[outer_idx] inner_idx = 0 while inner_idx < len(inner_list): if inner_list[inner_idx] == target: print(f"找到目标 {target} 在 [{outer_idx}][{inner_idx}]") found = True # 设置标志 break # 跳出内层循环 inner_idx += 1 outer_idx += 1 # 外层循环会检查 found 标志,如果为 True 则不再进入下一轮 if not found: print("未找到目标")

在这个例子中,found变量作为一个通信渠道,将内层循环的“找到结果”这一状态传递给了外层循环的条件判断,从而实现了协同退出。

5.2 使用函数和return提前退出

将嵌套循环的部分逻辑封装成函数,是更清晰、更模块化的做法。当需要从深层退出时,直接使用return即可跳出整个函数,也就自然退出了所有循环。

def find_in_2d_list(data, target): """在二维列表中查找目标值,找到即返回位置,否则返回None""" outer_idx = 0 while outer_idx < len(data): inner_idx = 0 while inner_idx < len(data[outer_idx]): if data[outer_idx][inner_idx] == target: return (outer_idx, inner_idx) # 直接退出整个函数 inner_idx += 1 outer_idx += 1 return None # 调用 result = find_in_2d_list([[1,2,3],[4,5,6],[7,8,9]], 5) if result: print(f"找到目标,位置:{result}")

这种方式比使用标志变量更简洁,也符合“单一职责原则”,将查找逻辑独立出来。

5.3 替代方案:itertools.product 与列表推导式

对于需要生成多重循环组合的场景,Python的itertools.product函数是绝佳的替代品。它可以生成多个可迭代对象的笛卡尔积,相当于多层for循环。

import itertools # 使用嵌套for循环 print("嵌套for循环:") for i in range(3): for j in range(2): print(f"({i}, {j})", end=' ') print() # 使用itertools.product print("itertools.product:") for i, j in itertools.product(range(3), range(2)): print(f"({i}, {j})", end=' ') print() # 输出两者完全相同。

优势

  • 代码更简洁:避免了多层缩进。
  • 可读性更强:一眼就能看出是在做组合迭代。
  • 灵活性高:可以轻松处理不定数量的循环嵌套。
  • 性能itertools模块的函数是C语言实现的,通常比手写Python循环更快。

对于简单的数据转换和过滤,列表推导式(或字典推导式、集合推导式)的嵌套形式也非常强大和简洁。

# 生成一个3x4的二维列表,值为行索引*10+列索引 matrix = [[i*10 + j for j in range(4)] for i in range(3)] print(matrix) # 输出: [[0, 1, 2, 3], [10, 11, 12, 13], [20, 21, 22, 23]] # 上面的列表推导式等价于以下嵌套循环: matrix = [] for i in range(3): row = [] for j in range(4): row.append(i*10 + j) matrix.append(row)

5.4 何时坚持使用while循环嵌套?

尽管有上述替代方案,while循环嵌套在以下场景中依然不可替代:

  1. 条件驱动而非数据驱动:当循环的继续与否取决于运行时动态变化的条件(如用户输入、传感器信号、网络连接状态),而非一个待遍历的集合时。
  2. 循环次数差异巨大且不可预知:内外层循环的次数没有固定关系,甚至内层循环的次数依赖于外层循环某次迭代的复杂计算结果。
  3. 需要复杂的循环控制:当循环流程中需要根据复杂逻辑,非标准地更新循环变量(如i += 2),或在循环体中多处使用break/continue来改变流程时,while循环提供的显式控制更清晰。
  4. 实现特定算法:某些算法(如一些搜索、解析、状态机算法)的伪代码或逻辑描述本身就采用了while循环嵌套的形式,直接翻译过来最直观。

6. 实战:模拟一个简单游戏循环

最后,我们用一个模拟“探险”的简单文本游戏来综合运用while循环嵌套。这个例子包含了游戏主循环、状态判断、用户交互和嵌套的任务循环。

import random def explore_room(room_name): """模拟探索一个房间,返回是否找到宝藏""" print(f"\n你进入了{room_name}。") chances = 3 # 搜索机会 found = False # 内层循环:在同一个房间内多次搜索 while chances > 0 and not found: action = input(f"你在{room_name}里做什么? (1)仔细搜索 (2)快速翻找 (3)离开: ") if action == '3': print("你决定离开这个房间。") break # 跳出内层循环(离开房间) if action not in ['1', '2']: print("无效指令。") continue # 模拟搜索结果 search_roll = random.randint(1, 10) # 仔细搜索成功率更高,但消耗更多机会 success_threshold = 8 if action == '1' else 9 chances_cost = 2 if action == '1' else 1 if search_roll >= success_threshold: print(f"太棒了!你在{room_name}的角落里找到了一个宝箱!") found = True else: print("这次搜索一无所获。") chances -= chances_cost print(f"剩余搜索机会: {chances}") return found # 游戏主循环 player_health = 10 rooms = ["昏暗的大厅", "布满灰尘的书房", "潮湿的地下室", "神秘的阁楼"] current_room_index = 0 treasures_found = 0 print("=== 古宅探险游戏 ===") print("你的目标是探索所有房间,并尽可能找到宝藏。") print("每个房间有有限的搜索机会。生命值归零游戏结束。\n") # 外层主循环:控制整个游戏进程 while player_health > 0 and current_room_index < len(rooms): current_room = rooms[current_room_index] print(f"\n生命值: {player_health} | 宝藏数: {treasures_found}") print(f"你当前在: {current_room}") # 模拟房间内可能遇到的危险事件 event_roll = random.randint(1, 5) if event_roll == 1: damage = random.randint(1, 3) player_health -= damage print(f"糟糕!你在{current_room}里绊了一跤,受到了 {damage} 点伤害。") if player_health <= 0: print("你伤重不支...") break # 游戏结束 # 调用房间探索函数(内含内层循环) if explore_room(current_room): treasures_found += 1 player_health = min(player_health + 2, 10) # 找到宝藏恢复生命 print("找到宝藏让你精神一振!生命值恢复了2点。") # 决定下一步行动 while True: # 一个小的输入验证循环 choice = input(f"\n接下来做什么? (1)进入下一个房间 (2)在此房间再搜搜 (3)查看状态 (4)退出游戏: ") if choice == '1': current_room_index += 1 break elif choice == '2': # 不增加current_room_index,下一轮外层循环继续探索当前房间 print("你决定再在这个房间看看。") break elif choice == '3': print(f"状态:生命{player_health}, 宝藏{treasures_found}, 房间{current_room_index+1}/{len(rooms)}") # 不break,继续显示选择菜单 elif choice == '4': print("游戏退出。") player_health = 0 # 通过设置生命值为0来退出外层主循环 break else: print("无效选择。") # 游戏结束判断 if player_health <= 0: print(f"\n游戏结束!你最终找到了 {treasures_found} 个宝藏。") elif current_room_index >= len(rooms): print(f"\n恭喜!你探索完了所有房间。共找到了 {treasures_found} 个宝藏。")

在这个游戏中,你可以清晰地看到三层循环结构:

  1. 最外层while循环while player_health > 0 and current_room_index < len(rooms)。这是游戏主循环,控制着整个游戏的进行条件(生命值>0且还有房间未探索)。
  2. 中间层while True循环:在玩家选择下一步行动时,用于输入验证,确保获得有效的指令后才继续。
  3. 内层while循环:在explore_room函数中,while chances > 0 and not found。这控制着在单个房间内的探索过程,直到机会用尽或找到宝藏。

这种结构使得游戏逻辑层次分明:主循环管大局,房间探索管局部交互,输入验证管用户指令。每个循环职责单一,通过条件变量(player_health,found,choice)进行通信和状态同步,是while循环嵌套在模拟复杂流程中的一个典型应用。

← 返回列表