☰
Python嵌套循环实战:从直角三角形到九九乘法表与金字塔
2026/10/17 5:48:36 网站建设 项目流程

很多刚接触Python的朋友,都会在练习册上碰到这一组题:用for循环打印直角三角形、九九乘法表、金字塔。我第一次看到时也不以为然,觉得不就是print几个星号嘛。真正动手写才发现,每一行要输出几个字符、什么时候换行、怎么对齐,全是循环边界和字符串拼接的细节。这套题的核心价值,不在于画图本身,而在于让你把“重复动作”拆成“行”和“列”两层去理解,为后续处理列表、矩阵、文本排版这类问题打基础。这篇文章按我自己当年练习的顺序来写,适合零基础、刚学完if和for的初学者,也适合想给新手讲清楚嵌套循环的教学场景。

1. 先把问题拆成“行”和“列”

1.1 嵌套循环到底在干什么

很多人第一次写嵌套循环时,容易把两层for想得很玄。其实它和现实中的排队很像:外层循环负责“换下一行”,内层循环负责“把当前这一行从头到尾填充完”。打印一排星号,相当于在一面墙上从左往右贴星星;打印多排星号,是先贴完一排,再回到最左边贴下一排。

用代码表达就是:

for i in range(1, 6): # 外层循环:控制行数 for j in range(i): # 内层循环:控制这一行打印几个 print('*', end='') print() # 一行结束,换行

内层的range(i)决定了“这一行打几个字符”,外层的range(1, 6)决定了“一共打几行”。end=''是让每次print不要自动换行,这样多个星号才能连成一排;后面那个单独的print()才是真正换行的地方。如果漏掉最后的print(),所有星号会全部挤在一行里。

1.2 每一行其实都有一个规律

这三道图形题,表面是画图,本质是找“行号 i 和目标图案的对应关系”。直角三角形第 i 行有 i 个星号,金字塔第 i 行有 2*i-1 个星号,九九乘法表第 i 行有 i 个等式。只要把每行的数量规律找出来,内层循环的范围就有了。

以 n=5 的直角三角形为例:

  • 第1行:打1个星号
  • 第2行:打2个星号
  • 第3行:打3个星号
  • 第4行:打4个星号
  • 第5行:打5个星号

所以内层循环次数就是range(i)。这里的i如果从1开始,range(i)正好产生0到i-1,长度为i。如果从0开始,就是range(i+1)。这个“差一位”很容易把人绕进去,我建议记住一个习惯:写成for i in range(1, n+1),内层直接range(i),思路最顺。

1.3 统一的三层结构

不管打印哪个图案,只要涉及多行、每行多个字符,结构基本都是三层:

for i in range(起点, 终点, 步长): # 第1步:打印行首空格(金字塔、右对齐三角需要) # 第2步:打印本行内容(星号或公式) # 第3步:print() 换行

这个模板可以覆盖后面所有的变体。先考虑“空格数”,再考虑“内容数”,最后补一个换行。很多人写金字塔前总觉得难,其实拆成这3步就不难了。

2. 直角三角形:从固定行数到逐行变化

2.1 标准左下版本

最常见的直角三角形是左下角对齐,也就是星号从少到多,左边缘对齐。代码可以这样写:

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

输出:

* ** *** **** *****

我第一次写时,把内层写成range(i+1),结果第一行就打了2个星号。原因是用range(i+1)时,i=1 会生成 [0,1] 两个值,星号数比行数多1。所以这里一定要让内层范围和行号保持一致:第 i 行就循环 i 次。最稳妥的验证方法,是把 n 改成 2 和 3 各跑一次,看看每行星号数量是否等于行号。

2.2 四个方向的变体

“直角三角形”其实有四个朝向:左下、左上、右下、右上。很多人不知道,只要调换外层循环的起点/终点,以及是否打印空格,就能自由切换。

变体外层循环每行空格数每行星号数
左下从1到n0i
右上从1到nn-ii
左上从n到10i
右上(另一种)从n到1n-ii

这里我把“右下”和“右上”的表头稍微区分一下,方便对应代码。最常用的两个变体是右下和左上:

右下三角形:星号仍然从少到多,但右边对齐,所以每行要先打印空格,再打印星号。

n = 5 for i in range(1, n + 1): for k in range(n - i): print(' ', end='') for j in range(i): print('*', end='') print()

左上三角形:星号从多到少,左边缘对齐。外层循环改成range(n, 0, -1),内层仍然range(i):

n = 5 for i in range(n, 0, -1): for j in range(i): print('*', end='') print()

如果你把左下、左上、右下、右上这四个都亲手写一遍,对range的步长和“先空格后内容”的理解会一下子提升不少。

2.3 实操细节:别小看print的end参数

很多人在这里踩的第一个坑,就是忘记end=''。默认情况下,print每打印一个字符就换行,结果输出被拉成一条竖直的星号列,完全不是三角形。第二个坑是缩进混乱。

Python用缩进区分代码块,内层循环里的print必须比外层for多缩进,换行用的print必须和外层for同级。如果缩进错了,程序要么报错,要么逻辑全乱。我习惯一律用4个空格缩进,并且不要混用Tab和空格。

第三个坑是星号和星号之间有没有空格。如果你写的是print('* ', end=''),输出会变成每个星号后面带一个空格,图形看着更大,但和标准直角三角形不同。题目要求的是连续星号,所以用'*'就好。

3. 九九乘法表:在嵌套循环里做格式化

3.1 从逐行公式推导循环结构

九九乘法表的本质,是把“第 i 行、第 j 列”的乘法公式打印出来。行数从1到9,列数从1到当前行号i。也就是说内层循环范围是range(1, i+1),外层是range(1, 10)。

先写一版最基础的:

for i in range(1, 10): for j in range(1, i + 1): print(f'{j}*{i}={i*j}', end=' ') print()

输出会是这样:

1*1=1 1*2=2 2*2=4 1*3=3 2*3=6 3*3=9 ...

注意公式里的写法:j在前,i在后,表示“较小的数乘较大的数”。如果写成f'{i}*{j}={i*j}',那么同一行会出现2*1=2、2*2=4这样的顺序,虽然结果没错,但不符合人们阅读乘法表的习惯。很多新手在这里忽略顺序,导致看起来乱。

3.2 对齐技巧:\t与格式化字符串的取舍

上面这个版本有个问题:当积是一位数或两位数时,公式长度不同,只用end=' '隔开,列会扭来扭去。要让九行对齐,核心是用制表符或指定宽度。

用end='\t'是最直白的办法:

for i in range(1, 10): for j in range(1, i + 1): print(f'{j}*{i}={i*j}', end='\t') print()

这样每个公式后面跟一个制表位,终端解析时会把它们推到相对整齐的列。虽然不同终端宽度可能让效果略有差异,但对初学者来说足够。如果追求更稳定,可以给每个公式一个固定宽度,比如:<4表示左对齐、占4个字符宽度:

for i in range(1, 10): for j in range(1, i + 1): print(f'{j}*{i}={i*j:<4}', end='') print()

这里的:<4是关键:把公式整体在4个字符宽度里左对齐。我看到很多人问为什么用{:<4}而不是{:>4},其实左对齐更符合乘法表的阅读习惯,每个公式从相同起点开始,积的位数变化不会导致错位。右对齐则会让数字个位对齐,风格不同。

3.3 上三角和完整矩形变体

九九乘法表不只有下三角这一种。把内层循环的终点固定成9,就能打印完整矩形:

for i in range(1, 10): for j in range(1, 10): print(f'{j}*{i}={i*j:<4}', end='') print()

如果想打印上三角,也就是每一行从当前行号开始到9,可以这样:

for i in range(1, 10): for j in range(i, 10): print(f'{j}*{i}={i*j:<4}', end='') print()

但这里有个细节:上三角通常希望右边缘对齐,也就是前面空出已经跳过的列。如果直接range(i, 10),每行开头直接是公式,整体是左对齐的上三角,不是居中的上三角。若想完全对齐,需要先打印空格占位。这个可以作为一个进阶练习,先打印空公式占位,再打印真实公式,效果更接近常见课本里的“右上三角”。

for i in range(1, 10): for k in range(1, i): print(' ', end='') # 每个空位占8个字符 for j in range(i, 10): print(f'{j}*{i}={i*j:<4}', end='') print()

不过这种写法需要统一空位宽度和公式宽度,否则依然对不齐。不要被这层细节吓到,先能把标准下三角打印好,就掌握了核心思路。

4. 金字塔:空格和星号的数学关系

4.1 从半个金字塔到完整金字塔

金字塔比直角三角形多了“居中”这个要求。先看一个更简单的过渡:每行星号数按奇数递增,左对齐,像是半个金字塔:

n = 5 for i in range(1, n + 1): for j in range(2 * i - 1): print('*', end='') print()

输出:

* *** ***** ******* *********

这其实就是“金字塔”去掉左边空格后的样子。要让星号居中,必须让右边的每行都比上一行少一些:左边的空格数等于总行数减当前行号。总行数是 n,当前行号是 i,所以空格数是n - i。这是整个金字塔实现里我最想强调的公式,一旦想通,代码就水到渠成。

4.2 把规律翻译成代码

完整金字塔代码:

n = 5 for i in range(1, n + 1): for k in range(n - i): print(' ', end='') for j in range(2 * i - 1): print('*', end='') print()

输出:

* *** ***** ******* *********

用 n=5 来验证一下:

  • 第1行:空格数 5-1=4,星号数 2*1-1=1
  • 第2行:空格数 5-2=3,星号数 2*2-1=3
  • 第3行:空格数 5-3=2,星号数 2*3-1=5

星号数比上一行多2,正好是奇数递增。空格逐渐减少1,整体看起来中心线对齐。很多教材会直接把空格循环放在星号循环前面,逻辑上就是“先往后推几格,再画星号”。

4.3 把星号换成数字或字母

理解了空格和星号的关系,金字塔就能炸出很多花样。比如打印数字金字塔,每行打印当前行号的数字:

n = 5 for i in range(1, n + 1): for k in range(n - i): print(' ', end='') for j in range(2 * i - 1): print(i, end='') print()

输出:

1 222 33333 4444444 555555555

如果想打印递增数字,比如第i行从1到i再倒回来,内层就要多一层判断或拼接字符串。这是金字塔练习的经典扩展,能让人更熟悉字符串和range的组合。初学者通常先把星号金字塔打出来,再试数字版,会有一种“原来如此”的通透感。

关于金字塔,我还有一个建议:不要只盯着星号。把星号换成字母“A”或者任意字符,代码结构完全一样,但会更容易看出空格是否算对。想要验证居中对齐,可以临时把空格改成.符号,比如print('.', end=''),输出会清晰地展示每一行前面有几个点。检查完后,再把点改回空格。

5. 常见问题与排查技巧:循环输出错乱怎么办

5.1 高频问题速查表

这些题目看起来简单,实际练习中我见过的问题五花八门。我把最典型的整理成一张表,方便随时对照。

现象原因解决办法
所有星号全在一行缺少换行用的print()在内层循环结束后加一个独立print()
每行星号比预期多1个range(i+1)误写成多一位用range(i),并验证行号与数量一致
金字塔向左歪没有打印空格,或空格数写错空格数应为n - i,且放在星号循环之前
乘法表各列对不齐只用end=' ',没有控制宽度改用end='\t'或:<4格式化
内层循环用了全局变量j忘记定义或外层变量被覆盖使用独立变量名,或在循环内初始化
外层循环从0开始,行号混乱对range的区间理解不清记住range(1, n+1)才是1到n

这里有很多是“边界差一”问题,看起来是小错误,但会让整个图形错乱。最有效的排查办法,是拿一张纸把n=3时的期望输出写下来,然后对照程序的每一行输出。

5.2 打印中间变量:让循环过程显形

遇到循环结果和预期不符时,别光盯着最终输出。把循环变量打印出来,一步一个脚印地看它怎么走。

比如打印直角三角形时,临时改成:

for i in range(1, 6): for j in range(i): print(f'i={i}, j={j}', end=' | ') print()

这样就能看到每一行有哪些j值,从而验证内层循环到底跑了几次。排查完再改回打印星号的版本。这个方法对任何嵌套循环都适用。

检查空格时,print(' ')肉眼看不清到底是一个空格还是两个空格,可以用repr():

print(repr(' ')) # 输出 ' '

或者直接把空格换成点,比如:

for k in range(n - i): print('.', end='')

这样输出就变成:

....* ...*** ..***** .******* *********

数一下点的数量,马上就知道空格有没有算对。我至今还用这种“显形法”排查加宽类的排版问题。

5.3 练习时我会故意改参数

很多人写完一个正确版本就停下来了,我反而会劝你多“破坏”几次。怎么破坏?就是故意改边界、改步长、改打印内容,看会输出什么。

比如把金字塔的外层循环改成range(0, n),这个依赖行号公式的地方就会全乱;把乘法表里的i+1改成i,会发现最后一行少一列;把三角形的end=''去掉,会看到竖线变成竖列。这些错误见一次,印象就深一层,后面面试或考试时遇到类似题目就不会慌。

我自己带过不少新手,发现一个通病:只追求“代码能跑”,不回头想“为什么能跑”。图形题是少有的、能够直观看到代码效果的练习,特别适合用来体会循环执行顺序。建议多花时间在“改参数、看变化”上,这比抄十遍代码更有用。

5.4 把n设为变量,养成不写死数字的习惯

我在所有示例里都用n或range(1, 10)控制范围,而不直接写range(1, 6)。因为图案的行数一旦写死,想改成10行就要改好几处。用变量 n 统一管理,改一个地方就能控制全部。

尤其是金字塔,空格、星号、外层行数都用同一个 n,代码可读性会好很多:

n = 5 for i in range(1, n + 1): for _ in range(n - i): print(' ', end='') for _ in range(2 * i - 1): print('*', end='') print()

循环变量用_表示“这里不关心具体值”,能让代码更清爽。这个写法在很多算法题里都很常见,建议提前习惯。

这几道打印题,我前前后后陪过不少朋友练习,自己也重写过很多次。坦白说,九九乘法表和金字塔的对齐,是最容易让新手卡住的地方。我后来总结了一个笨办法:遇到复杂图形,先用'.'把空格占位打出来,确认空格数没算错,再换成真实内容;遇到嵌套循环搞不清走向,就把循环变量打印出来看。这个习惯到现在写稍微复杂一点的列表处理时还在用。图形题本身不难,但它逼着你把循环、边界、格式化这些基础功练扎实,后面学列表推导式、二维矩阵遍历时,会非常自然。如果练完还有余力,试试把金字塔改成菱形,或者把乘法表改成右上三角版本,一通百通。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询