python学习(一)

学习参考书籍
编写比较通用的一个可以画出像图中那样花朵的函数集。
在这里插入图片描述

import math
import turtle
def polyline(t, n, length, angle):
    """Draws n line segments.
    t: Turtle object
    n: number of line segments
    length: length of each segment
    angle: degrees between segments
    """
    for i in range(n):
        t.fd(length)
        t.lt(angle)
        
def arc(t, r, angle):
    """Draws an arc with the given radius and angle.
    t: Turtle
    r: radius
    angle: angle subtended by the arc, in degrees
    """
    arc_length = 2 * math.pi * r * abs(angle) / 360
    n = int(arc_length / 4) + 3
    step_length = arc_length / n
    step_angle = float(angle) / n
    # making a slight left turn before starting reduces
    # the error caused by the linear approximation of the arc
    t.lt(step_angle/2)
    polyline(t, n, step_length, step_angle)
    t.rt(step_angle/2)

def petal(t, r, angle):
    """Draws a petal using two arcs.
    t: Turtle
    r: radius of the arcs
    angle: angle (degrees) that subtends the arcs
    """
    for i in range(2):
        arc(t, r, angle)
        t.lt(180-angle)

def flower(t, n, r, angle):
    """Draws a flower with n petals.
    t: Turtle
    n: number of petals
    r: radius of the arcs
    angle: angle (degrees) that subtends the arcs
    """
    for i in range(n):
        petal(t, r, angle)
        t.lt(360.0/n)

def move(t, length):
    """Move Turtle (t) forward (length) units without leaving a trail.
    Leaves the pen down.
    """
    t.pu()
    t.fd(length)
    t.pd()
    
bob = turtle.Turtle()
# draw a sequence of three flowers, as shown in the book.
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()

运行结果:
在这里插入图片描述
在这里插入图片描述
编写比较通用的一个可以画出图中那样图形的函数集。

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)
	
def polypie(t,n,r):
'''
画出饼状图函数
'''
    angle = 360.0 / n
    for i in range(n):
        isosceles(t, r, angle/2)
        t.lt(angle)
def draw_pie(t,n,r):
'''
画完后将光标右移
'''
    polypie(t, n, r)
    t.pu()
    t.fd(r*2 + 10)
    t.pd()
bob = turtle.Turtle()

bob.pu()
bob.bk(130)
bob.pd()

# draw polypies with various number of sides
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()

运行结果:
在这里插入图片描述

一、封装

将一部分代码包装在函数里被称作 encapsulation(封装)。 封装的好处之一,为这些代码赋予一个名字, 这充当了某种文档说明。另一个好处是,如果你重复使用这些代码, 调用函数两次比拷贝粘贴函数体要更加简洁!
函数功能:画一个正方形

def square(t):
    for i in range(4):
        t.fd(100)
        t.lt(90)
square(bob)

二、泛化

为函数增加一个形参被称作泛化(generalization), 这使得函数更通用。
函数功能:在前面的版本中, 正方形的边长总是一样的;此版本中,它可以是任意大小。

def square(t, length):
    for i in range(4):
        t.fd(length)
        t.lt(90)

square(bob, 100)

函数功能:可以画任意的正多边形

def polygon(t, n, length):
    angle = 360 / n
    for i in range(n):
        t.fd(length)
        t.lt(angle)

polygon(bob, 7, 70)

三、接口设计

函数功能:下一个练习是编写接受半径r作为形参的 circle 函数。 下面是一个使用 polygon 画一个50边形的简单解法。

import math

def circle(t, r):
    circumference = 2 * math.pi * r
    n = 50
    length = circumference / n
    polygon(t, n, length)

函数的第一行通过半径r计算圆的周长,公式是2πr。 由于用了 math.pi ,我们需要导入 math 模块。 按照惯例,import 语句通常位于脚本的开始位置。n是我们的近似圆中线段的条数, length 是每一条线段的长度。 这样 polygon 画出的就是一个50边形,近似一个半径为r的圆。这种解法的一个局限在于,n是一个常量,意味着对于非常大的圆, 线段会非常长,而对于小圆,我们会浪费时间画非常小的线段。 一个解决方案是将n作为形参,泛化函数。 这将给用户(调用 circle 的人)更多的掌控力, 但是接口就不那么干净了。
函数的接口(interface)是一份关于如何使用该函数的总结: 形参是什么?函数做什么?返回值是什么? 如果接口让调用者避免处理不必要的细节,直接做自己想做的事,那么这个接口就是“干净的”。
在这个例子中,r 属于接口的一部分,因为它指定了要画多大的圆。 n就不太合适,因为它是关于 如何 画圆的细节。
与其把接口弄乱,不如根据周长(circumference)选择一个合适的n值:

def circle(t, r):
    circumference = 2 * math.pi * r
    n = int(circumference / 3) + 1
    length = circumference / n
    polygon(t, n, length)

现在线段的数量,是约为周长三分之一的整型数, 所以每条线段的长度(大概)是3,小到足以使圆看上去逼真, 又大到效率足够高,对任意大小的圆都能接受。

四、重构

当我写 circle 程序的时候,我能够复用 polygon , 因为一个多边形是与圆形非常近似。 但是 arc 就不那么容易实现了;我们不能使用 polygon 或者 circle 来画一个弧。
一种替代方案是从复制 polygon 开始, 然后将它转化为 arc 。最后的函数看上去可像这样:
函数功能:画圆弧

    arc_length = 2 * math.pi * r * angle / 360
    n = int(arc_length / 3) + 1
    step_length = arc_length / n
    step_angle = angle / n

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

现在,我们可以用 polyline 重写 polygon 和 arc :

    angle = 360.0 / n
    polyline(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 / n
    step_angle = float(angle) / n
    polyline(t, n, step_length, step_angle)

最后,我们可以用 arc 重写 circle :

def circle(t, r):
    arc(t, r, 360)

重新整理一个程序以改进函数接口和促进代码复用的这个过程, 被称作重构(refactoring)。 在此例中,我们注意到 arc 和 polygon 中有相似的代码, 因此,我们“将它分解出来”(factor it out),放入 polyline 函数。

五、开发方案

开发计划(development plan)是一种编写程序的过程。 此例中我们使用的过程是“封装和泛化”。 这个过程的具体步骤是:

从写一个没有函数定义的小程序开始。
一旦该程序运行正常,找出其中相关性强的部分,将它们封装进一个函数并给它一个名字。
通过增加适当的形参,泛化该函数。
重复1–3步,直到你有一些可正常运行的函数。 复制粘贴有用的代码,避免重复输入(和重新调试)。
寻找机会通过重构改进程序。 例如,如果在多个地方有相似的代码,考虑将它分解到一个合适的通用函数中。
这个过程也有一些缺点。后面我们将介绍其他替代方案, 但是如果你事先不知道如何将程序分解为函数,这是个很有用办法。 该方法可以让你一边编程,一边设计。

六、文档字符串

文档字符串(docstring)是位于函数开始位置的一个字符串, 解释了函数的接口(“doc”是“documentation”的缩写)。 下面是一个例子:

def polyline(t, n, length, angle):
    """Draws n line segments with the given length and
    angle (in degrees) between them.  t is a turtle.
    """
    for i in range(n):
        t.fd(length)
        t.lt(angle)

按照惯例,所有的文档字符串都是三重引号(triple-quoted)字符串,也被称为多行字符串, 因为三重引号允许字符串超过一行。
它很简要(terse),但是包括了他人使用此函数时需要了解的关键信息。 它扼要地说明该函数做什么(不介绍背后的具体细节)。 它解释了每个形参对函数的行为有什么影响,以及每个形参应有的类型 (如果它不明显的话)。
写这种文档是接口设计中很重要的一部分。 一个设计良好的接口应该很容易解释, 如果你很难解释你的某个函数,那么你的接口也许还有改进空间。

七、调试

接口就像是函数和调用者之间的合同。 调用者同意提供合适的参数,函数同意完成相应的工作。

例如,polyline 函数需要4个实参:t 必须是一个 Turtle ; n 必须是一个整型数; length 应该是一个正数; angle 必须是一个数,单位是度数。

这些要求被称作先决条件(preconditions), 因为它们应当在函数开始执行之前成立(true)。 相反,函数结束时的条件是后置条件(postconditions)。 后置条件包括函数预期的效果(如画线段)以及任何其他附带效果 (如移动 Turtle 或者做其它改变)。

先决条件由调用者负责满足。如果调用者违反一个(已经充分记录文档的!) 先决条件,导致函数没有正确工作,则故障(bug)出现在调用者一方,而不是函数。

如果满足了先决条件,没有满足后置条件,故障就在函数一方。如果你的先决条件和后置条件都很清楚,将有助于调试。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值