4. 案例研究-接口程序

4. 案例研究-接口程序

本章通过一个案例研究, 来展示设计互相配合的函数的过程.
4.1 turtle 模块
创建一个文件mypolygon.py, 并输入如下代码:
import turtle
bob = turtle.Turtle()
print(bob)# 这一句的作用是让画板停留, 等手动点击x关闭画板, 程序才结束.
# 否则程序执行完毕画板就关闭了, 执行速度非常快, 画板出来0.几秒就关闭.
turtle.mainloop()
turtle模块()提供一个叫作Turtle的函数(), 它会创建一个Turtle对象(戏称乌龟), 我们将其赋值到bob变量.

image-20230307121854578

这意味着bob变量引用着在turtle模块中定义的Turtle类型的一个对象.
mainioop告诉窗口去等待用户进行某些操作, 虽然现在除了关闭窗口之外, 并没有提供给用户多少有用的操作.
创建一个好Turtle对象之后, 就可以调用它的一个方法(method)来在窗口中移动.
方法和函数类似, 但是使用的语法略有不同, 操作箭头向前(方向默认从左往右)移动: bob.fd(100), 完整代码:
import turtle
bob = turtle.Turtle()
print(bob)# 箭头向前移动100像素.
bob.fd(100)# 让画板停留.
turtle.mainloop()

image-20230307123711321

这个方法fd和bob(tuetle对象)是关联的. 调用方法和发出一个请求类似: 请求bob向前移动, (箭头向前移动).
fd的参数是移动的距离, 以像素(pixel)为单位, 所以实际移动的距离依赖于屏幕的分辨率.
Turtle对象的其它方法:
bk: 用于前进和后退.
lt: 控制向左旋转的角度, 单位是度.
rt: 控制向右旋转的角度, 单位是度.Turtle对象控制一只笔, 可以朝上和朝下, 若笔朝下, 则会制出走过的痕迹.
pu: 箭头朝上, 画笔没有痕迹.
pd: 箭头朝下, 画笔有痕迹,
import turtlebob = turtle.Turtle()px = 100  # 半径
bob.fd(px)  # 箭头向前移动100pxbob.pu()  # 笔尖朝上, 没有痕迹.
bob.fd(px)  # 箭头向前移动100pxbob.pd()  # 笔尖朝下, 没有痕迹.
bob.fd(px)  # 箭头向前移动100pxturtle.mainloop()

image-20230309011533245

编写一个程序, 创建Turtle对象, 先让箭头向前移动100px, 左转90, 在向前移动100px.
import turtle
bob = turtle.Turtle()
print(bob)# 前进100px.
bob.fd(100)# 箭头向左旋转90度
bob.lt(90)# 箭头前进100px
bob.fd(100)# 让画板停留.
turtle.mainloop()

image-20230307130139751

运行程序之后会看到这个箭头, 先向东走100px, 在向北走100px, 留下两个条线.
修改程序, 画出一个正方形:
import turtledef lt_fd(angle, px):# 箭头向左旋转xxx度, 走 n px.bob.lt(angle)bob.fd(px)# 实例对象.
bob = turtle.Turtle()# 前进100px.
bob.fd(100)# 箭头向左旋转90度, 走100px.
lt_fd(90, 100)
lt_fd(90, 100)
lt_fd(90, 100)# 让画板停留.
turtle.mainloop()

image-20230307154003658

4.2 简单重复
你可会写下如下代码:
import turtlebob = turtle.Turtle()# 前进100px.
bob.fd(100)
# 箭头向左旋转90度.
bob.lt(90)# 前进100px.
bob.fd(100)
# 箭头向左旋转90度.
bob.lt(90)# 前进100px.
bob.fd(100)
# 箭头向左旋转90度.
bob.lt(90)# 前进100px.
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):# 前进100px.bob.fd(100)# 箭头向左旋转90度.bob.lt(90)
import turtlebob = turtle.Turtle()for i in range(4):# 前进100px.bob.fd(100)# 箭头向左旋转90度.bob.lt(90)# 让画板停留.
turtle.mainloop()
这个版本的代码和之前的绘制正方形代码其实还稍有不同, 因为在最后一次循环, 它多做了一次左转.
多余的左转稍微多消耗了点时间, 但因为每次循环做的事情都一样, 也让代码更加简练,
程序执行完之后, 箭头回归到初始的位置, 并朝向初始相同的方向.
4.3 练习
下面是一系列使用Turtle的练习. 它们力求有趣, 但也包含着某些寓意(做练习时, 可以猜一下其寓意). 
1.1 写一个函数square, 接收一个形参t, 用来表示一个Turtle对象, 利用Turtle来画一个正方形.
1.2 写一个函数, 调用时, 传入的bob做为实参, 来调用square函数.
import turtle
# 实例turtle对象.
bob = turtle.Turtle()# 画正方形的函数.
def square(t):for i in range(4):t.fd(100)t.lt(90)# 调用函数, 将turtle对象作为实参进行传递.
square(bob)
# 让画板停留.
turtle.mainloop()
import turtle# 实例turtle对象.
bob = turtle.Turtle()# 画正方形的函数.
def square(t):for i in range(4):t.fd(100)t.lt(90)def func(t):square(t)# 调用函数, 将turtle对象作为实参进行传递.
func(bob)
# 让画板停留.
turtle.mainloop()
2. 给square函数再添加一个形参length, 修改内容, 保证正方形的长度是length, 并修改函数调用以提供这第二个实参, 最后提供不同的length值测试你的程序.
import turtle
# 实例turtle对象.
bob = turtle.Turtle()# 画正方形的函数.
def square(t, length):for i in range(4):t.fd(length)t.lt(90)# 调用函数, 将turtle对象作为实参进行传递.
square(bob, 100)
# 让画板停留.
turtle.mainloop()
3. 复制square函数,, 并命名为polygon, 再添加一个形参n并修改函数体, 以绘制一个正n变形.提示: 正n变形的拐角是360/n度, 是几变形就需要移动几次.
import turtle
# 实例turtle对象.
bob = turtle.Turtle()# 画正n边形的函数.
def polygon(t, length, n):# 计算旋转的角度.angle = 360 / n# 循环n次.for i in range(n):t.fd(length)t.lt(angle)# 调用函数, 将turtle对象作为实参进行传递.
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 * r# 周长计算其边数的估计值公式:n = 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) + 1step_length = arc_length / nstep_angle = angle / npolyline(t, n, step_length, step_angle)# 画圆函数
def circle(t, r):# 第三次参数为360则是一个圆.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 = 50length = 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 * r# 计算n的值. circumference /  的结果肯能是一个数, int()舍弃小数部分.n = int(circumference /  3) + 1# 计算length的值.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) + 1step_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) + 1step_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) + 1step_length = arc_length / nstep_angle = angle / npolyline(t, n, step_length, step_angle)# 画圆函数
def circle(t, r):# 第三次参数为360则是一个圆.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 / 360n = int(arc_length / 4) + 3step_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()

2023-03-09_01738

2. 4.7节中arc的函数并不准确, 因为使用多边形模拟近似圆, 总是会在真实的圆之外.因此, Turtle画完线之后会偏离真确的目标几个像素的地方, 我的解决方案例展示了一种方法可以减少这种错误的效果,阅读代码并考虑是否合理, 如果你自己画图, 可能会发现它是如何生效的. * 不要去纠结这些东西, 目前没必要.
# 在画弧时,首先角度向左转了(step_angle / 2),即1/2近似多边形线段边的夹角值.
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
有兴趣可以看看, 百度好多详细的解说, 我就跳过了...
# arc.pyimport 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 / 360n = int(arc_length / 4) + 3step_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()

image-20230309114047282

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()

image-20230309114607269

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得到的图形:

image-20230309120211455

运行letter.py后需要按下按键上的字母, 它依据键入的字母画画, 得到的图形如下:

image-20230309120502172

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.0for i in range(n):t.fd(length)dtheta = 1 / (a + b * theta)t.lt(dtheta)theta += dtheta# create the world and bob
bob = turtle.Turtle()
draw_spiral(bob, n=1000)turtle.mainloop()

image-20230309120832404

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/26825.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

8.12 面要素符号化综述

文章目录 前言面要素介绍总结 前言 本章介绍如何使用矢量面要素符号化说明:文章中的示例代码均来自开源项目qgis_cpp_api_apps 面要素介绍 地理空间的要素分为点、线和面,对应的符号也分三类:Marker Symbol、Line Symbol和Fill Symbol&…

c#中上传超过30mb的文件,接口一直报404,小于30mb的却可以上传成功

在一次前端实现上传视频文件时,超过30mb的文件上传,访问接口一直报404,但是在Swagger中直接访问接口确是正常的,且在后端控制器中添加了限制特性,如下 但是却仍然报404,在apifox中请求接口也是报404, 网上说: 在ASP.NET Core中,配置请求过来的文件上传的大小限制通常…

生命在于学习——Python人工智能原理(3.4)

三、深度学习 7、过拟合与欠拟合 过拟合和欠拟合是所有机器学习算法都要考虑的问题。 (1)基本定义 a、欠拟合 欠拟合是指机器学习模型无法完全捕获数据集中的复杂模式,导致模型在新数据上的表现不佳,这通常是由于模型过于简单…

C++进阶,一文带你彻底搞懂左右值引用以及移动语义和完美转发!

目录 一、左值引用1.左值2.左值引用3.左值引用的用途(1)修改实参(2)减少拷贝(3)使用左值引用可以在外部修改对象内的成员变量的值 二、右值引用1.右值(1)纯右值(2&#x…

一文解答 | 代码签名证书怎么选

在当代软件开发中,代码签名证书对于确保软件的完整性、安全性及其可信度至关重要。它通过数字签名验证代码的来源和未被篡改的状态,向最终用户确保软件的可靠性。选择合适的代码签名证书既有利于保护软件开发商的声誉,也有助于建立用户对软件…

虚拟化 之三 详解 jailhouse(ARM 平台)的构建过程、配置及使用

嵌入式平台下,由于资源的限制,通常不具备通用性的 Linux 发行版,各大主流厂商都会提供自己的 Linux 发行版。这个发行版通常是基于某个 Linux 发行版构建系统来构建的,而不是全部手动构建,目前主流的 Linux 发行版构建系统是 Linux 基金会开发的 Yocto 构建系统。 基本环…

ChatGPT:自然语言处理的新纪元与OpenAI的深度融合

随着人工智能技术的蓬勃发展,自然语言处理(NLP)领域取得了显著的进步。OpenAI作为这一领域的领军者,以其卓越的技术实力和创新能力,不断推动着NLP领域向前发展。其中ChatGPT作为OpenAI的重要成果更是在全球范围内引起了…

go interface

package mainimport "fmt"// 接口 interface func main() {c : Chinese{} //创建一个中国人实例u : American{} //创建一个美国人实例greet(c) //中国人打招呼greet(u) //美国人打招呼 }// 接收具备SayHello接口能力的变量 func greet(s SayHello) {…

Vertical Layout 、Horizontal Layout 实验窗体自适应布局

实验目的 学习实验使用布局实现如下自适应界面 窗体邮件,布局设置为垂直布局 用同样的方法,添加groupbox,并右键设置为水平布局 拖入一个Horizontal Layout,然后拖入button,拖入 Horizontal Spacer 遇到一个问题&#…

如何将ai集成到radsystems项目中,在项目中引入ai

AI可以自动化重复性和低价值的任务,例如数据输入、文档处理、信息检索等,让员工能够专注于更具战略性和创造性的工作。通过引入AI驱动的聊天机器人或虚拟助手,可以提供24/7的客户支持,快速响应用户的问题,提高客户满意…

卡塔尔.巴林:海外媒体投放-宣发.发稿效果显著提高

引言 卡塔尔和巴林两国积极采取措施,通过海外媒体投放和宣发,将本国的商业新闻和相关信息传达给更广泛的受众。在这一过程中,卡塔尔新闻网、巴林商业新闻和摩纳哥新闻网等媒体起到了关键作用。通过投放新闻稿,这些国际化的媒体平…

CBoard开源数据可视化工具

CBoard开源数据可视化工具 文章目录 CBoard开源数据可视化工具介绍资源列表基础环境一、安装JDK二、安装Maven2.1、安装Maven2.2、配置Maven 三、安装Tomcat8四、安装MySQL5版本4.1、安装相关依赖4.2、二进制安装4.3、设定配置文件4.4、配置systemcatl方式启动4.5、访问MySQL数…

VMware安装ubuntu22.4虚拟机超详细图文教程

一 、下载镜像 下载地址:Index of /ubuntu-releases/22.04.4/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 二、创建虚拟机 打开VMware点击左上角文件,创建新的虚拟机,打开后如下图: 下一步,镜像文件就是…

超市陈列艺术:不仅仅是货品摆放,更是营销策略的体现

品类管理在门店落地的最直观表现就是单品的空间陈列管理,通过陈列细节的差异体现出门店的商品定位与策略。此文分析入木三分,值得学习。 在商品陈列的空间管理领域,不仅要考虑整体的空间陈列,也要对每个商品的空间陈列位置&#…

做了2年前端,盘点前端技术栈!大佬轻喷~

前言 自己写了快两年前端,但是大致总结一下哈哈哈哈我觉得这个话题蛮有意思的,可以看看大家的技术广度,可以进行分享和学习以及讨论所以这里说一下我对我的前端技术,做一下盘点和总结因为我的开发年限有限,所以我觉得…

焦化行业排放平台简介

在当今社会,环保事业日益受到人们的关注。焦化行业作为重要的工业领域之一,其排放问题一直是环保工作的重点。为了有效控制焦化行业的排放,实施焦化行业排放平台成为了必不可少的措施。朗观视觉小编将详细探讨焦化行业排放平台的实施范围&…

【复旦邱锡鹏教授《神经网络与深度学习公开课》笔记】线性分类模型损失函数对比

本节均以二分类问题为例进行展开&#xff0c;统一定义类别标签 y ∈ { 1 , − 1 } y\in\{1,-1\} y∈{1,−1}&#xff0c;则分类正确时 y f ( x ; w ) > 0 yf(x;w)>0 yf(x;w)>0&#xff0c;且值越大越正确&#xff1b;错误时 y f ( x ; w ) < 0 yf(x;w)<0 yf(x;…

ubtun虚拟机安装

选择镜像后启动 选择第一个回车 加载完成后 &#xff0c;进入Ubuntu安装界面&#xff0c;安装语言选择English&#xff0c;完成后按一下回车&#xff1a; 此时弹出安装器可更新提示&#xff0c;下方选项选择第二个Continue without updating&#xff08;不更新&#xff0c;继续…

升级和维护老旧LabVIEW程序

在升级老旧LabVIEW程序至64位环境时&#xff0c;需要解决兼容性、性能和稳定性等问题。本文从软件升级、硬件兼容性、程序优化、故障修复等多个角度详细分析。具体包括64位迁移注意事项、修复页面跳转崩溃、解决关闭程序后残留进程的问题&#xff0c;确保程序在新环境中的平稳运…

k8s中的pod域名解析失败定位案例

问题描述 我在k8s中启动了一个Host网络模式的pod&#xff0c;这个pod的域名解析失败了。 定位步骤 敲kubectl exec -it [pod_name] -- bash进入pod后台&#xff0c;查看/etc/resolv.conf&#xff0c;发现nameserver配的有问题。这里我预期的nameserver应该使用宿主机的&…