【Think Python】Python笔记(四)接口设计【使用turtle作为案例】

接口设计,主要是为了设计出相互配合的函数;

4.1 turtle模块

检查是否安装了turtle模块:
# turtle 模块(小写的t)提供了一个叫作 Turtle 的函数(大写的T),这个函数会创建一个 Turtle 对象
import turtle
bob = turtle.Turtle()
print(bob)
turtle.mainloop()	# mainloop 告诉窗口等待用户操作

这个时候会出现下面的窗口:【只要可以import即是代表已经安装了turtle模块】
在这里插入图片描述

turtle模块的基本函数
  • fd方法是前进,实参是像素距离;
  • bk方法是后退,实参是像素距离;
  • ltrt代表向左转和向右转,实参数角度;
  • pupd分别代表抬笔和落笔,无参数;

下面画一个直角:

import turtle
bob = turtle.Turtle()
bob.pd()
bob.fd(100)
bob.lt(90)
bob.fd(100)
turtle.mainloop()

在这里插入图片描述

4.2 简单的重复【for循环(loop)】

示例:
# 绘制一个正方形
import turtle
bob = turtle.Turtle()

def bob_draw():
    bob.pd()
    bob.fd(100)
    bob.lt(90)
    bob.fd(100)
    bob.lt(90)

for i in range(2):
    bob_draw()
    
turtle.mainloop()
语法:
# num代表重复的次数
for i in range(num):		# 冒号结尾的header
	'代码块'							# 缩进的语句体body
练习:
import turtle as T
import math
PI = math.pi

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

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

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

def circle(t, r):
    length = (2*PI*r) / 360
    polygon(t, length, 360)

def arc(t, r, angle):
    length = (2*PI*r)/360
    polygon(t, length, 360, angle)

bob = T.Turtle()

arc(bob, 100, 90)

4.3 封装

  • 将一部分代码放在函数中称之为封装(encapsulation);
  • 封装的好处之一,是可以为这些代码赋予一个名字,可以相当于某种文档说明【更容易理解阅读】;
  • 另一个好处,是减少冗余代码;

4.4 泛化

  • 为函数增加一个形参称之为泛化(generalization);因为这样使得函数更具有通用性;
  • 当函数有较多的形参的时候,这时候很难记住这些参数的具体顺序,这时候可以在调用函数的时候,加上 形参的名称:
polygon(bob, n=7, length=70)

这些称之为关键字实参【区别于关键字】,因为这些加上了形参名作为“关键字”;【实际上也提高了可读性】

4.5 接口设计

  • 下面是一个接受r作为形参的circle函数:
import math

def circle(t, r):
    circumference = 2 * math.pi * r
    n = 50
    length = circumference / n
    polygon(t, 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)

4.6 重构

  • 重新整理一个程序以改进接口和促进代码复用,被称作重构(refactoring)
  • 在一个项目开始的时候,你常常并不知道那么多,不能设计好全部的接口。 一旦你开始编码后,你才能更好地理解问题。

4.7 开发方案(development plan)

开发方案是编写程序的一个过程,这里我们用到了封装和泛化

  • 这个过程的步骤:
    1. 从写一个没有函数定义的小程序开始;
    2. 一旦该程序开始运行,找出其中强相关的部分,将他们封装在一个函数中,并取一个名字;
    3. 通过增加适当的形参,泛化这个函数;
    4. 重复1-3步,直到有一些可以正常运行的函数。复制粘贴有用的代码,避免重复输入和重新调试;
    5. 寻找机会通过重构改进程序;

4.8 文档字符串(docstring)

docstring是位于函数开始位置的一个字符串,它解释了函数的接口;

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),但是应该包含别人使用这个函数的时候需要了解的关键信息;
    • 说明这个函数做什么;【不包含具体细节】
    • 解释每个形参对函数的行为的影响;
    • 每个形参的类型【如果不明显】
  • 文档是接口设计中很重要的一部分,一个设计良好的接口应该很容易进行解释,如果一个函数很难进行解释,说明这个函数还有改进空间;

4.9 调试

  • 接口像是函数和调用者之间的合同;调用者同意提供合适的参数,函数同意完成相应的工作;
先决条件(Pre-conditions):调用者需要提供的参数;
后置条件(Post-conditions):函数预期的结果以及其他附带效果;
  • 先决条件由调用者负责满足。如果调用者违反一个(已经充分记录文档的!) 先决条件,导致函数没有正确工作,则故障(bug)出现在调用者一方,而不是函数;
  • 如果满足了先决条件,没有满足后置条件,故障就在函数一方。如果你的先决条件和后置条件都很清楚,将有助于调试。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值