4. 案例研究-接口程序
本章通过一个案例研究 , 来展示设计互相配合的函数的过程 .
4.1 turtle 模块
创建一个文件mypolygon . py , 并输入如下代码 :
import turtle
bob = turtle. Turtle( )
print ( bob)
turtle. mainloop( )
turtle模块 ( ) 提供一个叫作Turtle的函数 ( ) , 它会创建一个Turtle对象 ( 戏称乌龟 ) , 我们将其赋值到bob变量 .
这意味着bob变量引用着在turtle模块中定义的Turtle类型的一个对象 .
mainioop告诉窗口去等待用户进行某些操作 , 虽然现在除了关闭窗口之外 , 并没有提供给用户多少有用的操作 .
创建一个好Turtle对象之后 , 就可以调用它的一个方法 ( method ) 来在窗口中移动 .
方法和函数类似 , 但是使用的语法略有不同 , 操作箭头向前 ( 方向默认从左往右 ) 移动 : bob . fd ( 100 ) , 完整代码 :
import turtle
bob = turtle. Turtle( )
print ( bob)
bob. fd( 100 )
turtle. mainloop( )
这个方法fd和bob ( tuetle对象 ) 是关联的 . 调用方法和发出一个请求类似 : 请求bob向前移动 , ( 箭头向前移动 ) .
fd的参数是移动的距离 , 以像素 ( pixel ) 为单位 , 所以实际移动的距离依赖于屏幕的分辨率 .
Turtle对象的其它方法 :
bk : 用于前进和后退 .
lt : 控制向左旋转的角度 , 单位是度 .
rt : 控制向右旋转的角度 , 单位是度 . Turtle对象控制一只笔 , 可以朝上和朝下 , 若笔朝下 , 则会制出走过的痕迹 .
pu : 箭头朝上 , 画笔没有痕迹 .
pd : 箭头朝下 , 画笔有痕迹 ,
import turtlebob = turtle. Turtle( ) px = 100
bob. fd( px) bob. pu( )
bob. fd( px) bob. pd( )
bob. fd( px) turtle. mainloop( )
编写一个程序 , 创建Turtle对象 , 先让箭头向前移动 100 px , 左转 90 度 , 在向前移动 100 px .
import turtle
bob = turtle. Turtle( )
print ( bob)
bob. fd( 100 )
bob. lt( 90 )
bob. fd( 100 )
turtle. mainloop( )
运行程序之后会看到这个箭头 , 先向东走 100 px , 在向北走 100 px , 留下两个条线 .
修改程序 , 画出一个正方形 :
import turtledef lt_fd ( angle, px) : bob. lt( angle) bob. fd( px)
bob = turtle. Turtle( )
bob. fd( 100 )
lt_fd( 90 , 100 )
lt_fd( 90 , 100 )
lt_fd( 90 , 100 )
turtle. mainloop( )
4.2 简单重复
你可会写下如下代码 :
import turtlebob = turtle. Turtle( )
bob. fd( 100 )
bob. lt( 90 )
bob. fd( 100 )
bob. lt( 90 )
bob. fd( 100 )
bob. lt( 90 )
bob. fd( 100 )
turtle. mainloop( )
使用for语句 , 可以更紧凑地实现同样功能 , 把下面的例子运行一次 :
for i in range ( 4 ) : print ( 'Hello!' )
运行工具窗口显示 :
Hello !
Hello !
Hello !
Hello !
for语句的语法和函数的定义类似 , 它也有一个以冒号结束的语句头 , 还有一个缩进的语句体 .
语句体可以包含任意数量的语句 .
for语句也被称为循环 ( loop ) , 因此执行流程会遍历语句体 , 之后从语句体的最开头重新循环执行 . 这是for语句的最简单用法 , 后面我们会看到更多的用法 .
但这样已经足够重写刚才的正方形程序了 , 使用for循环绘制正方形的程序 :
for i in range ( 4 ) : bob. fd( 100 ) bob. lt( 90 )
import turtlebob = turtle. Turtle( ) for i in range ( 4 ) : bob. fd( 100 ) bob. lt( 90 )
turtle. mainloop( )
这个版本的代码和之前的绘制正方形代码其实还稍有不同 , 因为在最后一次循环 , 它多做了一次左转 .
多余的左转稍微多消耗了点时间 , 但因为每次循环做的事情都一样 , 也让代码更加简练 ,
程序执行完之后 , 箭头回归到初始的位置 , 并朝向初始相同的方向 .
4.3 练习
下面是一系列使用Turtle的练习 . 它们力求有趣 , 但也包含着某些寓意 ( 做练习时 , 可以猜一下其寓意 ) .
1.1 写一个函数square , 接收一个形参t , 用来表示一个Turtle对象 , 利用Turtle来画一个正方形 .
1.2 写一个函数 , 调用时 , 传入的bob做为实参 , 来调用square函数 .
import turtle
bob = turtle. Turtle( )
def square ( t) : for i in range ( 4 ) : t. fd( 100 ) t. lt( 90 )
square( bob)
turtle. mainloop( )
import turtle
bob = turtle. Turtle( )
def square ( t) : for i in range ( 4 ) : t. fd( 100 ) t. lt( 90 ) def func ( t) : square( t)
func( bob)
turtle. mainloop( )
2. 给square函数再添加一个形参length , 修改内容 , 保证正方形的长度是length , 并修改函数调用以提供这第二个实参 , 最后提供不同的length值测试你的程序 .
import turtle
bob = turtle. Turtle( )
def square ( t, length) : for i in range ( 4 ) : t. fd( length) t. lt( 90 )
square( bob, 100 )
turtle. mainloop( )
3. 复制square函数 , , 并命名为polygon , 再添加一个形参n并修改函数体 , 以绘制一个正n变形 . 提示 : 正n变形的拐角是 360 /n度, 是几变形就需要移动几次 .
import turtle
bob = turtle. Turtle( )
def polygon ( t, length, n) : angle = 360 / nfor i in range ( n) : t. fd( length) t. lt( angle)
polygon( bob, 100 , 6 )
turtle. mainloop( )
4. 写一个函数circle函数 , 接收代表turtle的形参t , 以及表示半径的形参r , 并使用合适的长度和边数调用polygon画一个近似的圆 , 使用不同的r值来测试你的函数 . 提示 : 思考圆的周长 ( circumference ) , 并保证 : length * n = circumference .
import turtle
import mathbob = turtle. Turtle( ) def polygon ( t, n, length) : angle = 360 / nfor i in range ( n) : t. fd( length) t. lt( angle) def circle ( t, r) : circumference = 2 * math. pi * rn = int ( circumference / 3 ) + 1 length = circumference / npolygon( t, n, length) circle( bob, 10 )
turtle. mainloop( )
n = int ( circumference / 3 ) + 1
此代码根据正多边形的周长计算其边数的估计值 .
多边形的每一条边的长度相等 , 因此多边形的总周长等于边数与每条边长度的乘积 .
为了估计边数 , 代码将周长除以每条边长度的近似值 ,
该长度通过将周长除以 3 获得 , 然后使用“ + 1 ”将结果值向上舍入到最接近的整数 . 对于具有大量边的面 , 生成的估计值可能不准确 , 因为随着边数的增加 , 边长的近似值将变得不准确 .
但是 , 对于边数相对较少的面 , 估计值应相当准确 .
5. 给circle函数写一个更通用的版本 , 称为arc , 增加一个形参angle , 用来表示画的圆弧的大小 , 这里angle的单位是度数 , 所以当angle = 360 时 , 则会画一个整圆 .
import turtle
import mathbob = turtle. Turtle( )
def polyline ( t, n, length, angle) : for i in range ( n) : t. fd( length) t. lt( angle)
def polygon ( t, n, length) : angle = 360 / npolyline( t, n, length, angle)
def arc ( t, r, angle) : arc_length = 2 * math. pi * r * angle / 360 n = int ( arc_length / 3 ) + 1 step_length = arc_length / nstep_angle = angle / npolyline( t, n, step_length, step_angle)
def circle ( t, r) : arc( t, r, 360 ) circle( bob, 100 )
turtle. mainloop( )
4.4 封装
第一个练习要求把画正方形的代码放到一个函数定义中 , 并将turtle对象bob作为实参传入 , 调用该函数 .
下面一个解答 :
def square ( t) : for i in range ( 4 ) : t. fd( 100 ) t. lt( 90 ) square( bob)
最内侧的语句 , fd和lt都缩进了两层 , 表达它们在for语句的语句体内 , 而for语句在函数定义的函数体内部 .
最后一行 , square ( bob ) , 有重新从左侧开始而没有缩进 , 这表明for语句和square函数的定义都已经结束 . 在函数体内 , t引用的turtle对象和bob引用的相同 , 所有t . lt ( 90 ) 和直接调用bob . lt ( 90 ) 是一样的效果 .
在这种情况下为什么不直接把形参写为bob呢? 原因是t可以是任何turtle对象 , 而不仅仅是bob ,
所有可以再新建一个turtle对象 , 并将它作为参数传入到square函数 :
alice = Turtle( )
square( alice)
import turtlebob = turtle. Turtle( )
alice = turtle. Turtle( ) def square ( t) : for i in range ( 4 ) : t. fd( 100 ) t. lt( 90 ) square( bob)
square( alice) turtle. mainloop( )
把一段代码用函数包裹起来 , 称为封装 ( encapsulation ) .
封装的一个好处是 , 它给这个段代码一个有意义的名称 , 增加了可读性 .
另外一个好处是 , 当重复使用这段代码时 , 调用一个函数比复制粘贴代码要简易很多 .
4.5 泛化
下一步是给square函数添加一个length参数 , 这个是一个解决方案 :
def square ( t, length) : for i in range ( 4 ) : t. fd( length) t. lt( 90 ) square( bob, 100 )
import turtlebob = turtle. Turtle( ) def square ( t, length) : for i in range ( 4 ) : t. fd( length) t. lt( 90 ) square( bob, 100 ) turtle. mainloop( )
给函数添加参数的过程称为泛化 ( generalization ) , 因为它会让函数变得更通用 :
在之前的版本中 , 正方形总是一个大小 , 而形的版本中 , 可以是任意大小 ( 给length传递不同的值实现 ) . 下一步也是一次泛化 , 我们不再值绘制正方形 , 而是可以绘制任意边数的多边形 . 这里有一个答案 :
def polygon ( t, n, length) : angle = 360 / nfor i in range ( n) : t. fd( length) t. lt( angle) polygon( bob, 7 , 70 )
这个例子绘制一个 7 边形 , 边长为 70.
如果函数的形参比较多 , 很容易忘掉每一个参数具体是什么 , 或者忘掉它们的顺序 .
所有在Python中 , 调用函数时可以加上形参的名称 , 这样是合法的 , 并且有时候会有帮助 :
polygon( bob, n= 7 , length= 70 )
这些参数被称为关键字参数 ( keyword argument ) , 因为他们使用 '关键字' 的形式带上了形参的名称调用 ,
( 请别while与def之类的Python关键字混淆 ) . 这个语法使得程序更加可读 , 它也同样提示了我们实参和形参的工作方式 :
当调用函数时 , 实参传入并赋值给形参 .
import turtlebob = turtle. Turtle( ) def polygon ( t, n, length) : angle = 360 / nfor i in range ( n) : t. fd( length) t. lt( angle) polygon( bob, 7 , 70 )
turtle. mainloop( )
4.6 接口设计
下一步是写画圆的circle函数 , 接收形参r , 表示圆的半径 .
下面试一个简单的例子 , 通过调用polygon函数画 50 边的多变形 :
import mathdef circle ( t, r) : circumference = 2 * math. pi * rn = 50 length = circumference / npolygon( t, n, length)
第一行计算半径为r的圆的周长 , 使用公式 2 πr , 因为需要使用math . pi , 所有需要导入math模块 .
依照惯例 , import语句一般都放在脚本的开头 . n是我们用于近似画圆的多边形的边数 , 所有length是每个边的长度 , 因此 , Polygon画出一个 50 边形 ,
近似与一个半径为r的圆 .
这个解决方案的缺点之一n是一个常量 , 因此对于很大的圆 , 多变形的边线太长 ,
对于小圆 , 我们又浪费时间去画过短的边线 .
解决办法之一是泛化这个函数 , 将上形参n , 这样可以给用户 ( 调用circle函数的人 ) 更多的控制选择 ,
但接口就不那么清晰整洁了 .
函数的接口是如何使用它的概要说明 : 它有哪些参数? 这个函数做什么? 它的返回值是什么?
我们说一个接口 '整洁' ( clen ) , 是它能够让调用者完成所想的事情 , 而不需要处理多余的细节 . 在这个例子里 , r属于函数的接口 , 因为它指定了所画的圆的基本属性 .
相对地 , n则不那么适合 , 因为它说明的是如何画圆的细节信息 . 所以与其弄乱接口 , 不如在代码内部根据周长来选择合适的n值 .
def circle ( t, r) : circumference = 2 * math. pi * rn = int ( circumference / 3 ) + 1 length = circumferenc / npolygon( t, n, length)
显示多边形是一个接近 circumference / 3 + 1 的整型 , 所有每个边长近似是 3 ,
已经小到足够画出好看的圆形 , 但由足够大到不影响画线效率 , 并且可接收任何尺寸的圆 .
4.7 重构
当写circle函数时 , 可以复用polygon , 因为边数很多的正多边形 , 是圆的很好近似 ( 使用正多边形得到近似圆 ) .
但是arc则并不那么容易对付 , 我们不能使用polygon和circle来画圆弧 .
换个方法 , 可以复制一个polygons函数 , 在通过修改得到arc函数 , 结果如下 :
def arc ( t, r, angle) : arc_length = 2 * math. pi * angle / 360 n = int ( arc_length / 3 ) + 1 step_length = arc_length / nstep_angle = angle / nfor i in range ( n) : t. fd( step_length) t. lt( step_angle)
这个函数的第二部分很像polygon的实现 , 但如果不修改polygon的接口 , 无法直接复用 .
我们也可以泛化polygon函数以接受第三个参数表示圆弧的角度 ,
但那样的话polygon ( 多边形 ) 就是不合适的名称了 !
所有 , 我们将这个更泛化的行数称为polyline ( 多边线 ) :
def polyline ( t, n, length, angle) : for i in range ( n) : t. fd( length) t. lt( angle)
现在我们可以重写polygon和arc , 让他们调用polylin :
def polygon ( t , n, length) : angle = 360 / npolyline( t, n, length, angle) : def arc ( t, r, angle) : arc_length = 2 * math. pi * angle / 360 n = int ( arc_length / 3 ) + 1 step_length = arc_length / nstep_angle = angle / npolyline( t, n, step_length, step_angle)
最后 , 我们可以重写circle , 改为调用erc :
def circle ( t, r) : arc( t, r, 360 )
import turtle
import mathbob = turtle. Turtle( )
def polyline ( t, n, length, angle) : for i in range ( n) : t. fd( length) t. lt( angle)
def polygon ( t, n, length) : angle = 360 / npolyline( t, n, length, angle)
def arc ( t, r, angle) : arc_length = 2 * math. pi * r * angle / 360 n = int ( arc_length / 3 ) + 1 step_length = arc_length / nstep_angle = angle / npolyline( t, n, step_length, step_angle)
def circle ( t, r) : arc( t, r, 360 ) circle( bob, 100 )
turtle. mainloop( )
这个过程-重构组织程序 , 以改善接口 , 提高代码复用被称为重构 ( refactoring ) .
在这个例子里 , 我们注意到arc和polygon中有类似的代码 , 因此我们把它们的共同之处 '重构出来' ,
抽取到polyline函数中 ( 指的是for循环语句 ) .
如果我们早早计划 , 可能会直接先写下polyline , 也就避免了重构 ,
但实际上在工程开始时我们往往并没有足够的信息去完美的设计所有的接口 .
开始编码之后 , 你会更了解面对的问题 , 有时候 , 重构正意味着你在编程中掌握了一些新的东西 .
4.8 一个开发计划
开发计划 ( development plan ) 是写程序的过程 , 本章的案例分析中 , 我们使用的过程是 '封装和泛化' .
这个过程的具体步骤是 :
1. 最开始写一些小程序 , 而不需要函数定义 .
2. 一旦程序成功运行 , 识别出其中一段完整的部分 , 将它封装到一个函数中 , 并加以命名 .
3. 泛化这个函数 , 添加合适的形参 .
4. 重复步骤 1 到步骤 3 , 直到得到一组可行的函数 , 复制粘贴代码 , 以免重复输入 ( 以免重复调试 ) . 复制书写正确的代码 , 不要手打了 , 手打就无法避免出错 . . .
5. 寻找可以使用重构来改善程序的机会 . 例如 , 如果发现程序中几处地方有相似的代码 , 可以考虑将它们抽取去来做一个合适的通用函数 . 这个过程也是有一些缺点-我们会在后面看到其它方式 , 但如果在开始编程时不清楚如何将程序分成适合的函数 ,
这样做会带来帮助 . 这个方法能让你一边开发一边设计 .
4.9 文档字符串
文档字符串 ( docstring ) 是在函数开头用来解释其接口的字符串 ( doc是 '文档' documentation的缩写 ) .
下面是一个实例 :
def polylint ( t, n. length, angle) : """用给定的长度和角度(以度为单位)绘制n个线段.t: turtle对象.n: 图形的边数.length: 每条边的长度.angle: 角度.""" for i in range ( n) : t. fd( length) t. lt( angle)
依照惯例 , 所有的文档字符串都使用三引号括起来 , 三引号字符串又称为多行字符串 .
因为三引号允许字符串跨行表示 . 文档字符串很简洁 , 但已经包含了其它人需要知道的关于函数的基本信息 .
它简明地简洁地解释了函数是做什么的 ( 而不涉及如何实现的细节 ) ,
它解释了每个形参对函数行为的影响效果 , 以及每个形参应有的类型 ( 如果其类型并不显而易见 ) . 编写这类文档是接口设计的重要部分 , 一个设计良好的接口 , 也应当很简单就能解释清楚 .
如果你发现解释器一个函数很困难 , 很可能表示该接口有改进的空间 .
4.10 调试
函数的接口 , 作用就像是函数和调用者之间签订的一个合同 .
调用者同意提供某些参数 , 而函数则同意使用这些函数做某种工作 . 例如 : polyline需要 4 个参数 :
t : 必须是一个Turtle对象 ,
n : 必须是整数 .
length : 应当是个正数 .
angle : 则必须是一个数字 , 并且按照度数类理解 . 这些需求被称为前置条件 , 因为它们应当在函数开始执行之前就保证为真 .
相对地 , 函数结束的时候需要满足的条件称为后置条件 .
后置条件包含了函数预期的效果 ( 如画出线段 ) 以及任何副作用 ( 如移动箭头 , 或者引起其他改变 ) . 满足前置条件是调用者的职责 , 如果调用者违反了一个 ( 文档说明清晰的 ! ) 前置条件 ,
因而导致函数没有正确运行 , 则bug是在调用者 , 而不再函数本身 . 如果前置条件以及满足 , 但后置条件没有满足 , 那么bug就出现在函数本身 .
如果前置和后置都定义清晰 , 可以帮助调试 .
4.11 术语表
方法 ( method ) : 与某个对象关联的一个函数 , 使用句点表达式调用 . 循环 ( loop ) : 程序中的一个片段 , 可以重复运行 . 封装 ( encapsulation ) : 将一组语句转换为函数定义的过程 . 泛化 ( generalizatuon ) : 将一些不必要的具体值 ( 如一个数字 ) 替换为合适的通用参数或变量的过程 . 关键词参数 ( Keyword argument ) : 调用函数时 , 附带了参数名称 ( 作为一个 '关键词' 来使用 ) 的参数 . 接口 ( interface ) : 描述函数如何使用的说明 . 包含函数的名称 , 以及形参与返回值的说明 . 重构 ( refactoring ) : 修改代码并改善函数的接口以及代码质量的过程 . 开发计划 ( development plan ) : 写程序的过程 . 文档字符串 ( docstrinh ) : 在函数定义开始处出现的 , 用于说明函数接口的字符串 . 前置条件 ( precondition ) : 在函调用开始前应当满足的条件 . 后置条件 ( postcondition ) : 在函数调用结束后应当满足的条件 .
4.12 练习
1. 练习1
在 : https : / / github . com / AllenDowney / ThinkPython2 / blob / master / code / polygon . py 下载本章的代码 .
1. 画一个栈图来显示函数circle ( bob , radius ) 运行时的程序状态 . 你可以手动计算 , 或者在代码中添加一些pritn语句 .
import math
import turtlebob = turtle. Turtle( ) def polyline ( t, n, length, angle) : for i in range ( n) : t. fd( length) t. lt( angle) def polygon ( t, n, length) : angle = 360.0 / nprint ( 'polygon' , t, n, length, angle) polyline( t, n, length, angle) def arc ( t, r, angle) : arc_length = 2 * math. pi * r * angle / 360 n = int ( arc_length / 4 ) + 3 step_length = arc_length / nstep_angle = float ( angle) / nprint ( 'arc' , t, n, step_length, step_angle) polyline( t, n, step_length, step_angle) def circle ( t, r) : print ( 'cicle' , t, r) arc( t, r, 360 ) radius = 100
circle( bob, radius)
turtle. mainloop( )
2. 在 4.7 节中arc的函数并不准确 , 因为使用多边形模拟近似圆 , 总是会在真实的圆之外 . 因此 , Turtle画完线之后会偏离真确的目标几个像素的地方 , 我的解决方案例展示了一种方法可以减少这种错误的效果 , 阅读代码并考虑是否合理 , 如果你自己画图 , 可能会发现它是如何生效的 . * 不要去纠结这些东西 , 目前没必要 .
t. lt( step_angle/ 2 )
polyline( t, n, step_length, step_angle)
t. rt( step_angle/ 2 )
2. 练习2
写一组合适的通用函数 , 用来画出下图所示的花图案 ( 图在代码后面 ) .
解答 : https : / / github . com / AllenDowney / ThinkPython2 / blob / master / code / flower . py
另外也需要 : https : / / github . com / AllenDowney / ThinkPython2 / blob / master / code / polygon . py
有兴趣可以看看 , 百度好多详细的解说 , 我就跳过了 . . .
import math
import turtlebob = turtle. Turtle( ) def polyline ( t, n, length, angle) : for i in range ( n) : t. fd( length) t. lt( angle) def polygon ( t, n, length) : angle = 360.0 / nprint ( 'polygon' , t, n, length, angle) polyline( t, n, length, angle) def arc ( t, r, angle) : arc_length = 2 * math. pi * r * angle / 360 n = int ( arc_length / 4 ) + 3 step_length = arc_length / nstep_angle = float ( angle) / nprint ( 'arc' , t, n, step_length, step_angle) polyline( t, n, step_length, step_angle) def circle ( t, r) : print ( 'cicle' , t, r) arc( t, r, 360 ) def petal ( t, r, angle) : for i in range ( 2 ) : arc( t, r, angle) t. lt( 180 - angle) def flower ( t, n, r, angle) : for i in range ( n) : petal( t, r, angle) t. lt( 360.0 / n) def move ( t, length) : t. pu( ) t. fd( length) t. pd( ) move( bob, - 100 )
flower( bob, 7 , 60.0 , 60.0 ) move( bob, 100 )
flower( bob, 10 , 40.0 , 80.0 ) move( bob, 100 )
flower( bob, 20 , 140.0 , 20.0 ) bob. hideturtle( )
turtle. mainloop( )
turtle. mainloop( )
3. 练习3
写一组合适的通用函数 , 用来画出 4 - 2 所示的图形 .
解答 : https : / / github . com / AllenDowney / ThinkPython2 / blob / master / code / pie . py
import math
import turtledef draw_pie ( t, n, r) : polypie( t, n, r) t. pu( ) t. fd( r * 2 + 10 ) t. pd( ) def polypie ( t, n, r) : angle = 360.0 / nfor i in range ( n) : isosceles( t, r, angle / 2 ) t. lt( angle) def isosceles ( t, r, angle) : y = r * math. sin( angle * math. pi / 180 ) t. rt( angle) t. fd( r) t. lt( 90 + angle) t. fd( 2 * y) t. lt( 90 + angle) t. fd( r) t. lt( 180 - angle) bob = turtle. Turtle( ) bob. pu( )
bob. bk( 130 )
bob. pd( )
size = 40
draw_pie( bob, 5 , size)
draw_pie( bob, 6 , size)
draw_pie( bob, 7 , size)
draw_pie( bob, 8 , size) bob. hideturtle( )
turtle. mainloop( )
4. 练习4
字母表中的字母可以是一些基本元素来构成 , 如横线 , 竖线 , 以及一些曲线 .
设计一个字母表 , 可以使用最少的基本元素画出来 , 并编写函数来画出字母 .
你应当给每个字母单独写一个函 , 名称为draw_a , draw_b等 , 并把这些函数放到letter . py文件中 .
可以从 : https : / / github . com / AllenDowney / ThinkPython2 / blob / master / code / typewriter . py
下载一个 'Turtle打字机' 程序来帮你测试你写的代码 . ( 这个对于新手来说可能不理解 , 可以忽略调 . )
解答 : https : / / github . com / AllenDowney / ThinkPython2 / blob / master / code / letters . py
依赖的模块文件 : https : / / github . com / AllenDowney / ThinkPython2 / blob / master / code / polygon . py 直接复制代码运行一次就得了 , 有时间去研究 , 没时间跳过 , 不实用 . . .
运行letters . py得到的图形 :
运行letter . py后需要按下按键上的字母 , 它依据键入的字母画画 , 得到的图形如下 :
5. 练习5
在 http : / / en . wikipedia . org / wiki / Spiral 阅读关于螺旋线 ( spiral ) 的信息 , ( 国外的网站访问不了 ) .
接着编写一段程序画出阿基米德螺旋线 ( 或者其它的某种螺旋线 ) .
解答 : https : / / github . com / AllenDowney / ThinkPython2 / blob / master / code / spiral . py
import turtledef draw_spiral ( t, n, length= 3 , a= 0.1 , b= 0.0002 ) : """Draws an Archimedian spiral starting at the origin.Args:n: how many line segments to drawlength: how long each segment isa: how loose the initial spiral starts out (larger is looser)b: how loosly coiled the spiral is (larger is looser)http://en.wikipedia.org/wiki/Spiral""" theta = 0.0 for i in range ( n) : t. fd( length) dtheta = 1 / ( a + b * theta) t. lt( dtheta) theta += dtheta
bob = turtle. Turtle( )
draw_spiral( bob, n= 1000 ) turtle. mainloop( )