1. 从一道绘图题出发:绘制钝角三角形
海龟绘图是 Python 内置模块turtle提供的经典入门玩法,很多初学者第一周接触 Python 就会用它画出五角星、樱花树、螺旋线。把海龟绘图拆成 100 道练习题来训练,是一种很高效的刻意练习方式,因为它把数学概念、坐标方向、循环结构、函数封装揉在了一起。
第 12 题很常见:绘制一个钝角三角形。
要求一般是这样:
- 使用海龟绘图画一个钝角三角形;
- 三角形中必须有一个角大于 90 度且小于 180 度;
- 程序运行后能看到清晰、完整的三角形轮廓。
这道题看起来比画等边三角形难一点,但又不至于难到需要引入复杂几何算法。它真正考察的是两个核心点:
- 你是否真的理解海龟的“转向角度”和“三角形内角”之间的关系;
- 你是否能把“已知角度画三角形”这类问题做成一个通用的函数,而不是每次手动硬编码。
很多学员画钝角三角形时,照着网上的代码抄了一遍,能画出图形,但问一句“为什么转向150度而不是30度”就答不上来。本文就把这个问题一次讲清楚,并提供可直接运行的代码、逐步推导过程、常见踩坑点。
2. 环境准备与版本说明
开始写代码之前,先确认基础环境。这里以最常见的 Python 3 环境为例。
- 操作系统:Windows 10/11、macOS、Linux 均可;
- Python 版本:Python 3.8 及以上(使用 3.10+ 体验更好);
- 开发工具:IDLE、VS Code、PyCharm 都可以;
- 依赖模块:
turtle(Python 标准库,无需pip install)。
turtle模块是 Python 自带的标准库,也就是说安装好 Python 就自动拥有它。如果你还不知道本机是否具备条件,打开终端或命令提示符,输入下面的命令检查:
python --version如果显示类似:
Python 3.10.7就说明 Python 环境可用。接着输入:
python -c "import turtle; print('turtle ok')"如果输出:
turtle ok那就可以直接进入代码阶段了。如果这段命令报错ModuleNotFoundError,说明 Python 安装异常或被精简过,建议重新安装完整版 Python。
有一点要提醒新手:当你用 VS Code 或 PyCharm 运行海龟绘图程序时,程序通常会打开一个独立的图形窗口。如果你的电脑上报错No module named 'tkinter',说明 Python 缺少 Tk 图形支持,这在 Windows 官方安装包中默认自带,但在部分 Linux 精简环境中可能需要额外安装python3-tk。
3. 海龟绘图的基本逻辑
3.1 海龟模型是什么
海龟绘图可以这样理解:画布上有一支“画笔”,这支画笔带着一个小海龟图标。你可以命令小海龟前进、后退、左转、右转,它移动的轨迹就形成了图形。
常见指令如下:
import turtle t = turtle.Turtle() t.forward(100) # 前进 100 像素 t.left(90) # 左转 90 度 t.right(30) # 右转 30 度 t.backward(50) # 后退 50 像素小海龟的初始方向是水平向右,也就是屏幕右边。结合这一点,就能理解后面角度计算的方向问题。
3.2 画三角形的最小代码
用海龟画一个等边三角形非常简单:
import turtle t = turtle.Turtle() for _ in range(3): t.forward(100) t.left(120) turtle.done()为什么循环里是left(120)?因为等边三角形的每个内角都是 60 度,但海龟转向时转的是外角,也就是前进方向与下一段前进方向之间的夹角。三角形外角与内角互补:
外角 = 180 - 内角所以等边三角形的转向角度是180 - 60 = 120。
这个关系是解决第 12 题的关键。很多人画钝角三角形出错,大概率是直接在代码里写了钝角的度数,而没有先换算成外角。
4. 钝角三角形的内角与外角关系
4.1 什么是钝角三角形
三角形中有一个角大于 90 度且小于 180 度,就称为钝角三角形。
例如下面几个度数都可以构成钝角三角形:
角 A = 120 度,角 B = 30 度,角 C = 30 度 角 A = 100 度,角 B = 40 度,角 C = 40 度 角 A = 110 度,角 B = 40 度,角 C = 30 度三角形的内角和恒等于 180 度,所以只要一个角大于 90 度,另外两个角的和就会小于 90 度。
4.2 为什么绘制时要转外角
假设有一个三角形 ABC:
角 A = 120 度 角 B = 30 度 角 C = 30 度现在小海龟从 A 点出发,先沿 AB 方向前进,到达 B 点后,它需要转向 BC 方向。注意:小海龟当前朝向是“从 A 指向 B”,也就是沿着 AB 线段的方向。如果想让海龟沿着 BC 方向走,它转过的角度其实是从 AB 方向转向 BC 方向的那一个夹角,也就是 B 点处的外角,而不是三角形内部的角度。
B 点内角是 30 度,所以转向角度是:
180 - 30 = 150 度同理,到达 C 点后,C 点内角也是 30 度,转向150度。
最后从 C 点回到 A 点,A 点内角是 120 度,转向角度是:
180 - 120 = 60 度把三个转向角度加在一起:
150 + 150 + 60 = 360 度这符合“海龟绕封闭图形一周总旋转角度为 360 度”的规律,也是一个非常好的自检标准。
5. 第 12 题的第一种实现:直接绘制
明确了内角与外角的关系,就可以直接写第一版代码。
import turtle # 创建画笔 t = turtle.Turtle() t.speed(3) t.pensize(2) # 绘制钝角三角形 # 三个内角:A = 120 度, B = 30 度, C = 30 度 side_ab = 200 side_bc = 200 # 从 A 点出发,水平向右画 AB t.forward(side_ab) # 到达 B 点,转向角度 = 180 - 30 = 150 t.left(150) # 画 BC t.forward(side_bc) # 到达 C 点,转向角度 = 180 - 30 = 150 t.left(150) # 画 CA t.forward(200) turtle.done()5.1 代码逐行解析
创建一个海龟对象t,并设置速度与画笔粗细。
t.forward(side_ab)小海龟从 A 点向右画一条长为 200 像素的线段 AB。因为默认方向是朝右,所以 AB 是水平线。
到达 B 点后执行:
t.left(150)小海龟原本朝向右边,现在向左旋转 150 度。有同学会好奇:如果我在 B 点画一条辅助线(把 AB 延长过去),小海龟转的角度正好是 AB 延长线与 BC 的夹角,这确实就是 B 点的外角。外角等于内角补角,所以是180 - 30 = 150。
注意,这里用的是left。因为我们希望路径走向左上方。如果你用right(150),图形会往右下方偏,画出来的是镜像对称但同样钝角的三角形,这不会报错,只是位置看起来不同。
接着:
t.forward(side_bc) t.left(150)在 C 点再次左转 150 度,方向转向 A 点。
最后:
t.forward(200)回到 A 点,图形闭合。
5.2 运行结果
运行上面的代码,你会看到一个类似下图效果的三角形轮廓:
A / \ / \ / \ / \ B---------CA点在上方,B点在左侧,C点在右侧,角 A是顶部的钝角,角 B和角 C是底部的两个锐角。
这种写法的缺点是:边长、角度都写死了,如果题目要求你修改角度,就要改好几处,而且容易改乱。
6. 第二种实现:使用函数封装
实际练习时,我更推荐你把绘制过程封装成函数,这样后续会遇到类似的“画任意三角形”题目时直接复用。
先定义一个有实际意义的通用函数:
import turtle def draw_obtuse_triangle(t, angle_a, angle_b, side_ab): """ 根据两个角和一条边绘制一个钝角三角形 :param t: turtle 对象 :param angle_a: 第一个角 A 的度数 :param angle_b: 第二个角 B 的度数 :param side_ab: AB 边的长度(像素) """ # 由三角形内角和为 180 度,求出第三个角 C angle_c = 180 - angle_a - angle_b # 海龟画 AB 边 t.forward(side_ab) # 在 B 点转向:转向的是外角,所以是 180 - angle_b t.left(180 - angle_b) # 根据正弦定理计算第二条边的长度 import math # 这里的比例是 BC / sin(angle_a) = AB / sin(angle_c) side_bc = side_ab * math.sin(math.radians(angle_a)) / math.sin(math.radians(angle_c)) t.forward(side_bc) # 在 C 点转向,转向角度是 180 - angle_c t.left(180 - angle_c) # 最后画 CA 边,长度不一定要计算清楚,回到起点即可 t.forward(side_ab) # 使用示例 t = turtle.Turtle() t.speed(3) t.pensize(2) # 钝角三角形:角A = 120,角B = 30,角C = 30 draw_obtuse_triangle(t, angle_a=120, angle_b=30, side_ab=200) turtle.done()6.1 为什么用正弦定理计算边长
如果三角形三个角分别是 A、B、C,分别对应的边是 a、b、c,那么正弦定理告诉我们:
a / sin(A) = b / sin(B) = c / sin(C)在这段代码中,已知 AB 边的长度为side_ab。AB 边对着角 C,BC 边对着角 A,因此:
AB / sin(C) = BC / sin(A)所以:
BC = AB * sin(A) / sin(C)代码中用math.radians()把角度转为弧度,因为 Python 的math.sin()默认接收弧度值。
上面的实现比较接近真实编程场景:写函数时考虑通用性,而不是只为一次运行服务。
当然,回到第 12 题本身,题目一般没有要求计算第三边。实际上按照“从 A 出发,画完两条边后,最后回到 A”的思路,第三边并不需要知道精确长度,因为最后一段forward会把小海龟带回原 A 点。上面代码最后一行的t.forward(side_ab)其实只有在 C 点转向正确的前提下才刚好回到 A 点。
7. 第三种实现:用循环结构绘制
为了养成好习惯,可以把重复三次的动作用循环表达出来:
import turtle # 定义三角形的三个内角 angles = [120, 30, 30] # 定义三条边的长度,这里选用两边相等的等腰钝角三角形 sides = [200, 200, 200] t = turtle.Turtle() t.speed(3) t.pensize(2) for i in range(3): t.forward(sides[i]) t.left(180 - angles[i]) turtle.done()这种写法更接近“算法题”的思路:把三角形的三个顶点信息分别用列表存储,再通过一次循环完成绘制。
但这里有一个小坑:边长列表与角度列表的对应关系。在顶点 A 处画边 AB 后,顶点 A 处转向的角度是180 - 角A;然后画边 BC,在顶点 B 处转向180 - 角B;最后画边 CA,在顶点 C 处转向180 - 角C。所以循环里角度顺序是 A、B、C,对应的是在哪个顶点发生转向。
如果你给出的角度和边长不能构成一个真正的闭合三角形,这个简单的循环画出来的图形可能不会回到起点。因此,如果要通用循环,可以使用上面“正弦定理算边长”的方式。
8. 运行效果与程序结构对比
为了让运行效果更直观,可以把三种实现的效果合并来看:
| 实现方式 | 代码量 | 可读性 | 扩展性 | 推荐程度 |
|---|---|---|---|---|
| 直接顺序代码 | 短 | 一般 | 差 | 入门理解 |
| 函数封装 | 中等 | 好 | 好 | 练习必备 |
| 循环+列表 | 短 | 较好 | 中 | 算法练习 |
在实际的“海龟绘图 100 题”学习中,我建议你至少掌握函数封装这种写法,因为后续很多题目(菱形、多边形、复合图形)都要求把画出某个几何形状封装成可复用函数。
9. 常见问题与排查思路
9.1 画出来的不是三角形,而是不闭合折线
现象:图形看起来像是在走折线,最后没有回到起点。
可能原因:某个转向角度错误,导致后续路径偏离。
排查步骤:
- 检查三个内角之和是否为 180 度;
- 确认每个转向角度都用
180 - 内角计算; - 把代码中的长度改短到 100,配合
print输出当前海龟坐标,方便观察。
解决方案:
print(t.position())你可以在每条边绘制后打印海龟坐标,观察最后是否回到起点。
9.2 图形方向反了
现象:画出的三角形是向下的,或者向内凹陷。
可能原因:某次转向用了right而应该用left,或者反了过来。
解决方法:想一想你的海龟当前朝向是哪个方向。想让它往左上方走,就left;想让它在某些对称场景中往右下走,就right。两者都能画出钝角三角形,只是镜像方向不同。
9.3 窗口一闪而过
现象:运行程序后,图形窗口立即关闭。
原因:程序执行完所有绘图语句后直接退出。
解决方案:在代码末尾加:
turtle.done()如果使用 PyCharm,有时还需要在运行配置中勾选“Emulate terminal in output console”。
9.4 想要固定画布尺寸
在绘制一些复杂图形时,画布默认大小是 800×600 像素。如果图形较大,或者希望画面居中,可以使用:
turtle.setup(800, 600) turtle.screensize(800, 600)也可以使用:
turtle.setworldcoordinates(-400, -300, 400, 300)调整坐标系。不过对于第 12 题这种简单图形,默认画布通常够用。
下表整理了高频报错场景:
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 图形未闭合 | 转向角度错或边长不合理 | 核对内角/外角,打印坐标 |
| 图形方向相反 | left和right用混 | 明确当前朝向再选择方向 |
| 窗口闪退 | 缺少turtle.done() | 在末尾添加turtle.done() |
| 画笔线太粗/太细 | pensize设置不当 | 调整t.pensize() |
| 程序卡住不动 | 可能是死循环或窗口阻塞 | 检查循环次数,按 Esc 可退出 |
10. 从一题到一类:海龟绘图中角度计算的通用思维
第 12 题虽然只是画一个钝角三角形,但它背后的思维可以扩展到很多图形:
10.1 正多边形
画正 n 边形时,每个外角是 360 / n,例如:
import turtle def draw_polygon(t, n, side): for _ in range(n): t.forward(side) t.left(360 / n) t = turtle.Turtle() draw_polygon(t, 6, 100) # 正六边形 turtle.done()10.2 任意三角形
如果已知三个内角 A、B、C 和一条边 a,可以用正弦定理计算另外两条边,再配合转向角度绘制。
import turtle import math def draw_triangle(t, angle_a, angle_b, angle_c, side_a): """绘制一个已知三内角和一条边的三角形""" # 对应关系:a 边对着角 A,b 边对着角 B,c 边对着角 C angle_a_rad = math.radians(angle_a) angle_b_rad = math.radians(angle_b) angle_c_rad = math.radians(angle_c) side_b = side_a * math.sin(angle_b_rad) / math.sin(angle_a_rad) side_c = side_a * math.sin(angle_c_rad) / math.sin(angle_a_rad) # 画 a 边 t.forward(side_a) # 在 B 点转向 t.left(180 - angle_b) # 画 b 边 t.forward(side_b) # 在 C 点转向 t.left(180 - angle_c) # 画 c 边 t.forward(side_c)这样的函数对绘制任意三角形都适用。
10.3 复合图形
海龟绘图中经常出现“多个三角形拼在一起”的题目。比如一个菱形可以由两个等边三角形组成,一个五角星可以由五个钝角等腰三角形组成。明白了“在哪个顶点转多少度”之后,拆解复合图形就不再困难。
11. 让钝角三角形变得更好看
11.1 填充颜色
题目如果要求填充颜色,可以用begin_fill()和end_fill():
import turtle t = turtle.Turtle() t.speed(3) # 设置填充颜色 t.fillcolor("sky blue") t.begin_fill() angles = [120, 30, 30] sides = [200, 200, 200] for i in range(3): t.forward(sides[i]) t.left(180 - angles[i]) t.end_fill() turtle.done()这样绘制出的钝角三角形内部会填充上淡蓝色,视觉效果好很多。
11.2 标注顶点文字
使用t.write()可以在画布上写文字。例如在顶点 A、B、C 处标注字母:
import turtle t = turtle.Turtle() t.speed(3) # 画 AB 边 t.forward(200) t.write("B", font=("Arial", 16, "normal")) # 转向并画 BC t.left(150) t.forward(200) t.write("C", font=("Arial", 16, "normal")) # 转向并画 CA t.left(150) t.forward(200) t.write("A", font=("Arial", 16, "normal")) turtle.done()这样图形更直观,适合课堂演示。
11.3 让图形定位到画布中心
默认海龟从画布中央开始,所以直接画即可。如果图形需要偏移到某个位置,使用penup()和goto():
t.penup() t.goto(-100, -100) t.pendown()这个组合在画多个图形的题目中非常常用。
12. 代码风格与工程建议
12.1 命名要清晰
海龟绘图的代码虽然短,但仍然要有命名的好习惯:
# 不推荐 t.forward(200) t.left(150) t.forward(200) # 推荐 side_length = 200 turtle_turn_angle = 150 t.forward(side_length) t.left(turtle_turn_angle)12.2 函数单一职责
一个函数只做一件事。画三角形的函数就不要同时负责设置画布、填充颜色和写文字。后面这些功能用单独的函数处理,代码结构更清晰。
12.3 使用常量代替魔法数字
代码中直接出现的数值常被称为“魔法数字”,阅读时很难快速理解含义。可以把这三个直角数值抽成变量:
ANGLE_A = 120 ANGLE_B = 30 ANGLE_C = 30 turn_at_a = 180 - ANGLE_A turn_at_b = 180 - ANGLE_B turn_at_c = 180 - ANGLE_C12.4 保留推导注释
海龟绘图题目很容易忘记角度计算公式,建议在关键位置添加注释,方便日后回顾:
# 在某个顶点转向时,需要转 180 - 该点内角这种注释不是废话,而是记录了数学推导过程,对后续做复杂的正多边形、星星、分形图形很有帮助。
13. 课后思考与练习
第 12 题画钝角三角形,可做的扩展方向很多。建议你完成后继续挑战:
- 画一个钝角为 100 度的钝角三角形,要求另两个角不相等,例如
角 B = 50 度,角 C = 30 度,看看应该怎么设置边长和转角; - 画两个大小不同的钝角三角形,其中小三角形内嵌在大三角形内部,形成“嵌套三角形”效果;
- 利用
random模块随机生成一个钝角三角形的角度组合,然后绘制出来,观察图形变化; - 把钝角三角形的画法封装成
draw_obtuse_triangle(t, angle_a, side_ab)类函数,其中自动补全另外两个角; - 画一个由 5 个钝角三角形拼接而成的图案,例如类似“五角星”的形状。
每一道练习题都在反复强化同一个结论:海龟转向角度 = 180 度 - 内角。
你可以在自己的代码中做一个快速验证:把三个顶点转向角度相加,如果是 360 度,说明三角形的外角方向逻辑正确;如果相加结果不是 360 度,图形大概率不会闭合。
这种验证方式不仅适用于三角形,也适用于任意多边形。掌握它以后,“用海龟画多边形”这类问题基本就稳了。