文章目录

  • 5. 代码重复使用的艺术:函数
    • 5.1 一个例子引入
    • 5.2 定义函数
    • 5.3 使用函数重构代码
    • 请关注公众号:编程之路从0到1

5. 代码重复使用的艺术:函数

编程中的函数和我们数学中的函数内涵是不同的。简单说,编程中的函数,其实就是一组代码。那为什么要把这些代码放到一起呢?用任何语言来表达这个问题都会显得抽象和难以理解。我们不如直接回到没有函数的情况下去写代码,亲自体会发明函数的必要性。

5.1 一个例子引入

我们上次学习了如何使用“海龟”先生画图,那我们这次再复习一下用法

我们今天就来画下面这幅图

在画这种图之前,我们肯定先得画一个草图,计算好所有的坐标位置,我们用红色虚线表示坐标轴,两条虚线交点就是原点,如下

大家可以先自己画一画,代码如下

import turtle# 创建一个窗口对象,并赋值给变量window
window = turtle.Screen()# 创建一个海龟对象,并赋值给变量tt
tt = turtle.Turtle()# 抬起画笔
tt.penup()# 将画笔移动到坐标为(150,150)的位置
tt.goto(150, 150)# 落下画笔
tt.pendown()# 右转90度
tt.right(90)# 画外层的大长方形
tt.forward(450)
tt.right(90)
tt.forward(300)
tt.right(90)
tt.forward(450)
tt.right(90)
tt.forward(300)# 画顶部的直角三角形
tt.left(135)
tt.forward(212)
tt.left(90)
tt.forward(212)# 画中心的半圆形
tt.penup()
tt.goto(50, 0)
tt.pendown()
tt.left(225)
tt.circle(50, 180)# 画正方形窗户,需要注意,画完圆形后,画笔朝向已经向下
tt.penup()
tt.goto(50, 0)
tt.pendown()tt.forward(100)
tt.right(90)
tt.forward(100)
tt.right(90)
tt.forward(100)
tt.right(90)
tt.forward(100)# 画窗户中的十字
tt.penup()
tt.home()
tt.pendown()
tt.right(90)
tt.forward(100)tt.penup()
tt.goto(50, -50)
tt.pendown()
tt.right(90)
tt.forward(100)# 隐藏画笔(小三角)
tt.hideturtle()
# 不让窗口立刻退出
window.mainloop()

上面的代码密密麻麻写了60多行,我们仔细观察会发现,其中有很多是重复的代码,比如下面这一段

tt.right(90)
tt.forward(100)
tt.right(90)
tt.forward(100)
tt.right(90)
tt.forward(100)

我们人类的进步是建立在偷懒的基础上的,那就会有人想了,既然是重复的代码,那我们程序员能不能偷一下懒,少写一点呢?这里的例子只有几行重复代码,那如果是一个复杂的程序,有几百行重复的代码呢?这偷懒可不是省下一点点工作量,那是节省了大量的工作量。

这个时候就可以用上我们开头说的函数了,将相关代码放到一起,形成一组代码。修改代码如下

import turtle# 创建一个窗口对象,并赋值给变量window
window = turtle.Screen()# 创建一个海龟对象,并赋值给变量tt
tt = turtle.Turtle()# 抬起画笔
tt.penup()# 将画笔移动到坐标为(150,150)的位置
tt.goto(150, 150)
# 落下画笔
tt.pendown()
# 右转90度
tt.right(90)# 画外层的大长方形
tt.forward(450)
tt.right(90)
tt.forward(300)
tt.right(90)
tt.forward(450)
tt.right(90)
tt.forward(300)# 画顶部的直角三角形
tt.left(135)
tt.forward(212)
tt.left(90)
tt.forward(212)# 画中心的半圆形
tt.penup()
tt.goto(50, 0)
tt.pendown()
tt.left(225)
tt.circle(50, 180)# 画正方形窗户,需要注意,画完圆形后,画笔朝向已经向下
tt.penup()
tt.goto(50, 0)
tt.pendown()tt.forward(100)# tt.right(90)
# tt.forward(100)
# tt.right(90)
# tt.forward(100)
# tt.right(90)
# tt.forward(100)def repeat():tt.right(90)tt.forward(100)# 调用repeat函数
repeat()
repeat()
repeat()# 画窗户中的十字
tt.penup()
tt.home()
tt.pendown()
tt.right(90)
tt.forward(100)tt.penup()
tt.goto(50, -50)
tt.pendown()
tt.right(90)
tt.forward(100)# 隐藏画笔(小三角)
tt.hideturtle()
# 不让窗口立刻退出
window.mainloop()

如上,我们将重复的代码注释掉了(即不再生效),然后定义了一个函数叫repeat,这个函数块里面包含两句代码,这个函数就相当于是一个组,然后调用三次这个函数,就替代了之前被注释的那段代码。我们重新运行程序,功能和之前一模一样。

使用函数封装了一组代码,今后只需要调用这个函数就可以了,这里代码量少,看不出有什么优势,我们后面会接着封装。

5.2 定义函数

我们先学习一下函数的定义方式

def是单词define的缩写,定义的意思。要记住,Python中的所有缩进都是4个空格。这里我们没有定义参数,所以参数列表是空的。要注意,定了一个函数,这个函数里面的代码并不会执行,只有当调用函数时,函数里面的代码才会执行!调用函数就很简单了,我们已经调用过很多次函数了,只需要在函数名后面加一对因为括号就可以。如果这个函数有参数,那么需要在括号中传入参数。

先看一个简单示例

def add(x,y):print(x + y)add(19,1)

输出结果:
20

我们定义了一个加法函数,在参数列表处定义了两个参数xy,这说明调用这个函数时,必须传入两个参数,否则会报错。定义完成后,我们去调用这个函数,并传入两个参数。参数列表中的参数,其实就是一个变量,变量名可以自己取。

在定义一个函数时,应该将逻辑上的某一种功能封装成一个函数,而我们之前定义repeat函数时,只是简单的将重复代码放到一个函数而已,在逻辑上没有什么意义。但是后面定义的add函数就是有意义的,它对应的功能是将传入的参数相加,它是一个加法函数。

这一次我们就再深入一层,定义函数时,从功能角度去封装。首先观察我们一开始画的那幅图,我们发现它有一处重复的地方,它的最外层是一个三角形加长方形,内部呢,又有一个正方形的窗户。我们知道,长方形和正方形统称为矩形,这样我们就可以定义一个画矩形的函数,然后调用两次这个函数画两个矩形,不就完美了吗?

import turtle# 定义一个函数,画正方形。它有两个参数,第一个是画笔,第二个是边长
def draw_square(pen, length):pen.right(90)pen.forward(length)pen.right(90)pen.forward(length)pen.right(90)pen.forward(length)pen.right(90)pen.forward(length)# 创建一个窗口对象,并赋值给变量window
window = turtle.Screen()# 创建一个海龟对象,并赋值给变量tt
tt = turtle.Turtle()# 调用我们的draw_square函数,将画笔、正方形边长传进函数中
draw_square(tt, 100)tt.hideturtle()
# 不让窗口立刻退出
window.mainloop()

执行这段代码后成功的画了一个正方形,但是我们这个函数一点也不灵活,我们第二次调用的时候,它仍然在原地画了一个正方形,我们希望能传一个坐标,以这个坐标点为右上角,在指定的位置画正方形,另一个问题则是这个函数中仍然有重复的代码,我们再很早之前就讲过,重复的艺术——循环,要画四条边,那我们循环执行四次就可以了,再次改进代码

import turtle# 定义一个函数,画正方形。它有4个参数,第一个是画笔,第二、三是坐标,最后一个是边长
def draw_square(pen, x, y, length):# 抬起画笔pen.penup()# 复位画笔,主要是为了让画笔方向朝向右侧pen.home()# 将画笔移动到指定的点pen.goto(x, y)# 放下画笔pen.pendown()# 通过for循环让块中的代码循环运行四次for i in range(4):pen.right(90)pen.forward(length)# 创建一个窗口对象,并赋值给变量window
window = turtle.Screen()# 创建一个海龟对象,并赋值给变量tt
tt = turtle.Turtle()# 调用draw_square函数,将画笔、右上角x、y坐标,边长传入
draw_square(tt, 30, -100, 50)
draw_square(tt, 30, 100, 50)tt.hideturtle()
# 不让窗口立刻退出
window.mainloop()

运行上面的代码,我们成功的分别以(30, -100)、(30, 100)两个点为右上角画了两个边长为50的正方形。有了我们的draw_square函数,我们再也不需要去写那么多代码画正方形了,每次调用这个函数就可以了,这真是写一个函数,永远受益啊,大家完成这个函数,是不是特别有成就感呢!这里一定要好好体会一下,函数的妙用,以及对代码的封装。

这段代码只有一个地方需要简单说明一下,我们在函数中调用了pen.home(),这是因为每次调用draw_square函数时,我们无法确定画笔在此时的朝向,如果无法确定朝向,我们也根本没办法画正方形,因为我们的画法是默认画笔朝向右侧的,然后我们调用pen.right(90)将画笔转向朝下,然后从右上角开始朝下方画。pen.home()函数就是将画笔复位的意思,调用之后,不管画笔曾经是什么状态,此刻它都已经复位,处于原点位置,且朝向右侧。

到这里,离我们今天的目标已经很近了,我们前面订的目标是写一个画矩形的函数,现在只是完成了画正方形,那该如何改造,让这个函数也能画矩形呢?

import turtle# 定义一个函数,画矩形。它有5个参数,与之前不同的是,我们将边长拆成两个参数,表示宽(width)和高(height)
def draw_rectangle(pen, x, y, width, height):# 抬起画笔pen.penup()# 复位画笔,主要是为了让画笔方向朝向右侧pen.home()# 将画笔移动到指定的点pen.goto(x, y)# 放下画笔pen.pendown()if width == height:for i in range(4):pen.right(90)pen.forward(width)else:for i in range(2):pen.right(90)pen.forward(height)pen.right(90)pen.forward(width)# 创建一个窗口对象,并赋值给变量window
window = turtle.Screen()# 创建一个海龟对象,并赋值给变量tt
tt = turtle.Turtle()# 调用我们的draw_square函数,将画笔、正方形边长传进函数中
draw_rectangle(tt, 30, -100, 50, 50)
draw_rectangle(tt, 30, 100, 50, 90)tt.hideturtle()
# 不让窗口立刻退出
window.mainloop()

我们首先将函数名改为draw_rectangle,意思是画矩形。我们知道,正方形其实就是特殊的矩形,也就是边长相等的矩形。因此我们之前的draw_square函数其实改动不大,我们只是加了一个if-else判断,当宽和高相等时,说明是正方形,那就仍然走以前的代码,如果宽高不相等,那显然是长方形,由于长方形的特殊性,我们的代码只能循环两次,这一点结合上面的画法图很容易理解。最后我们调用draw_rectangle在指定坐标处各画了一个正方形和长方形

到这里我们彻底实现了定义一个函数画矩形的目标,大家一定要好好体会体会这里的妙用。

5.3 使用函数重构代码

最后,使用我们编写的函数,改造最开始我们画房子的代码

import turtle# 定义一个画矩形的函数
def draw_rectangle(pen, x, y, width, height):# 抬起画笔pen.penup()# 复位画笔,主要是为了让画笔方向朝向右侧pen.home()# 将画笔移动到指定的点pen.goto(x, y)# 放下画笔pen.pendown()if width == height:for i in range(4):pen.right(90)pen.forward(width)else:for i in range(2):pen.right(90)pen.forward(height)pen.right(90)pen.forward(width)# 创建一个窗口对象,并赋值给变量window
window = turtle.Screen()# 创建一个海龟对象,并赋值给变量tt
tt = turtle.Turtle()# 画一个300x450的矩形
draw_rectangle(tt, 150, 150, 300, 450)# 抬起画笔
tt.penup()
# 将画笔移动到坐标为(150,150)的位置
tt.goto(150, 150)
tt.pendown()# 画一个直角三角形
tt.left(135)
tt.forward(212)
tt.left(90)
tt.forward(212)tt.penup()
# 移动到(50,0)位置,画一个半圆
tt.goto(50, 0)
tt.pendown()
tt.left(225)
tt.circle(50, 180)# 画一个100x100的正方形
draw_rectangle(tt, 50, 0, 100, 100)# 画窗户中的十字
tt.penup()
tt.home()
tt.pendown()
tt.right(90)
tt.forward(100)tt.penup()
tt.goto(50, -50)
tt.pendown()
tt.right(90)
tt.forward(100)tt.hideturtle()
# 不让窗口立刻退出
window.mainloop()

改造后,代码从逻辑上就异常清晰了。这一章的学习中,我们要学习好函数的使用,充分体会函数的复用性,这种重复使用不仅仅是一次性的,而是今后我们再也不需要去手动的一笔一笔的画矩形了,甚至我们还能把这个函数发给别人使用。而且有了函数后,代码变得更加清晰,一个函数就对应一种功能,以后我们会不断学习函数的使用,写代码中灵活定义函数,这样我们的代码就有了结构,再也不是一锅粥的都写在一起,这就好比写文章我们要分段,绝不是一篇文章只有一段那样。

请关注公众号:编程之路从0到1

只要关注,即可获取网盘资源!
配合本教程,还有视频讲解哦,不容错过

给普通人的Python——第五章(上)相关推荐

  1. 【python第五章——函数】

    python第五章--函数 第五章--函数 5.1函数概述 5.2 函数进阶 下面部分之后再写 5.3函数习题(一些小例子) 第五章--函数 5.1函数概述 快速入门: #定义函数 def func( ...

  2. python 第五章 字典

    python 第五章 # # !/usr/bin/python # # -*- coding: utf-8 -*- # # @File : 总结.py # # @time : 2019-07-1010 ...

  3. javascript高级程序设计 学习笔记 第五章 上

      第五章   引用类型的值(对象)是引用类型的一个实例.在 ECMAScript 中,引用类型是一种数据结构, 用于将数据和功能组织在一起.它也常被称为类,但这种称呼并不妥当.尽管 ECMAScri ...

  4. python第五章课后题答案_python程序设计基础(嵩天)第五章课后习题部分答案

    原博文 2019-10-13 13:50 − 第五章p1515.2:实现isodd()函数,参数为整数,如果参数为奇数,返回true,否则返回false.def isodd(s): x=eval(s) ...

  5. python第五章总结

    第5章组合数据类型 5.1认识组合数据类型 (1)常用的序列类型有字符串.列表和元组. (2)序列支持双向索引:正向递增索引和反向递减索引正向递增索引从左向右依次递增,第一个元素的索引为0,第二个元素 ...

  6. Python第五章序列,集合,字典

    一.切片 介绍 1.函数float函数和eval函数的区别: 2. 利用下标"[]"可以从字符串(列表,元组)中截取一个子串 二.浅拷贝和深拷贝 1.浅拷贝 2.深拷贝 3.哈希 ...

  7. python第五章课后编程题答案_Python核心编程-第五章课后习题

    5-1 整形 讲讲 Python 普通整型和长整型的区别 答:在2.7版本基本淡化了区别.真要区分的话,普通整型是32位或者64位,而长整型只与PC内存有关,很大就是了 5-2 运算符 (a) 写一个 ...

  8. python第五章_Python数据分析-第5章Series(下)

    2,指数加权移动平均的意义 指数加权移动均值(EWMA,Exponentially Weighted Moving Average) 的公式是:EWMA(t) = aY(t) + (1-a)EWMA( ...

  9. python第五章课后题答案超星_MOOC网课章节答案_Python 语言程序设计见面课测试答案...

    [单选,A1型题] 能疏肝.暖肝,治肝寒气滞诸痛的是() [单选,A4型题,A3/A4型题] 男,32岁,进食后上腹饱胀不适,伴返酸.烧心.嗳气.食欲不振,临床诊断为慢性胃炎.该病治疗中不正确的是() ...

最新文章

  1. Cell子刊:辛秀芳组发现病原菌效应因子操纵植物ABA激素通路促进侵染的新机制...
  2. adc0808温度换算公式_温湿度传感器的三种模拟量换算关系
  3. 019_MySQL正则表达式
  4. Linux 查看I/O端口地址分配
  5. Java中利用socket实现简单的服务端与客户端的通信(基础级)
  6. redis-cli 命令详解
  7. LoadRunner 常用C语言函数使用
  8. 什么是SAP Spartacus schematics
  9. .bash_profile vs .bashrc
  10. 【转】20个让Web Developer开发生涯更加轻松的工具
  11. elementui 嵌套表单验证_elementUI 表单嵌套表格验证,日期选择器联动限制等写法
  12. 靠着零代码报表工具,转行报表开发后月薪超过3万
  13. android--04activity的布局管理器
  14. Linux之SELinux自主访问控制系统
  15. 茆诗松等《高等数理统计(第二版)》例 1.28 的错误及改正
  16. 四种XML解析方式详解
  17. Android Camera processCaptureResult 分析
  18. python二进制转十进制函数_python基础教程之python基础教程之python进制转换(二进制、十进制...
  19. 2020东三省数学建模A题
  20. W74 - 999、云计算工程师认证

热门文章

  1. BAT 前端开发面经 —— 吐血总结
  2. 微服务中网关的作用及搭建
  3. ceil 函数和 Floor 函数(C语言)
  4. matlab遗传算法先收敛后发散,遗传算法收敛问题
  5. php 上传到爱奇艺,php 爬虫爱奇艺 视频、内容
  6. w3cschoolHTML1
  7. 化妆品公司对电子商务的需求
  8. 取消管理员取得所有权_电脑系统如何取得管理员所有权
  9. 金蛇狂舞 (dfs 记忆化剪枝
  10. Android 进阶——性能优化之因Handler引起句柄泄漏导致ANR的定位和解决