函数的定义与使用
函数的理解与定义
一段代码的表示
- 一段具有特定功能、可重用的语句组
- 一种功能的抽象,一般函数表达特定功能
- 降低编程难度和代码复用
def <函数名>(0个或多个参数):
<函数体>
return <返回值>
案例:计算n!
def fact(n):
s = 1
for i in range(1,n+1):
s *= i
return s
- 函数定义时,所给的参数是一种占位符
- 函数定义后,如果不进行调用,不会被执行
- 函数是IPO的一种实现和完整代码的封装
函数的使用及调用过程
调用是运行函数代码的方式
a = fact(10)
print(a)
- 调用时要给出实际参数
- 实际参数替换函数中的参数
- 运行函数后得到返回值
函数的参数传递
函数可以有参数,也可以没有,但必须保留括号
函数定义是可以为某些参数指定默认值,构成可选参数:
def <函数名>(<非可选参数>,<可选参数>):
<函数体>
return <返回值>
案例:计算n!//m
def fact(n, m=1):
s = 1
for i in range(1,n+1):
s *= i
return s//m
可变参数传递
函数定义时可以设计可变数量参数,即不确定参数总数量
def <函数名>(<参数>, *b):
<函数体>
return <返回值>
b是参数名
案例:计算n!乘数
def fact(n, *b):
s = 1
for i in range(1,n+1):
s *= i
for item in b:
s *= item
return s
参数传递的两种方式
函数调用时,参数可以按照位置或名称方式传递
def fact(n, m=1):
s = 1
for i in range(1,n+1):
s *= i
return s//m
>>> fact(10,5)
725760
也可以带上名称
>>> fact9m=5, n=10)
725760
函数的返回值
可以返回0个或多个结果
-
return
保留字用来传递返回值 - 函数可以有返回值,也可以没有,可以有
return
,也可以没有 -
return
可以返回0个或多个值
案例:
def fact(n, m=1):
s = 1
for i in range(1,n+1):
s *= i
return s//m, n, m
返回的是元组类型
>>> fact(10,5)
(725760, 10, 5)
>>> a,b,c = fact(10,5)
>>>print(a,b,c)
725760 10 5
局部变量和全局变量
局部变量:函数内部使用的变量
全局变量:整个程序使用的变量
规则1:局部变量和全局变量是不同的变量
- 局部变量是函数内部的占位符,与全局变量可能重名但不同
- 函数运算结束后,局部变量被释放
- 可以使用
global
保留字在函数内部使用全局变量
n, s = 10, 100
def fact(n):
s = 1 #此处s是局部变量,与全局变量s不同
for i in range(1,n+1):
s *= i
return s #此处s是局部变量,值为3628800
print(fact(n), s) #此处s是全局变量,值为100
使用global
保留字:
n, s = 10, 100
def fact(n):
global s #声明此处的s为全局变量s
s = 1
for i in range(1,n+1):
s *= i
return s
print(fact(n), s) #此处全局变量s被函数修改
规则2:局部变量为为组合数据类型且未创建,等同于全局变量
ls = ['F', 'f'] #创建了一个列表类型ls
def func(a):
ls.append(a) #此处ls是列表类型,未真实创建则等同于全局变量
return
func('C') #全局变量ls被修改
print(ls)
运行结果
>>>
['F', 'f', 'C']
如果创建了一个ls
ls = ['F', 'f'] #创建了一个列表类型ls
def func(a):
ls = [] #此处ls是列表类型,真实创建
ls.append(a)
return
func('C') #局部变量ls被修改
print(ls)
运行结果
>>>
['F', 'f']
使用规则
- 基本数据类型,无论是否重名,局部变量与全局变量不同
- 可以通过
global
保留字在函数内部声明全局变量 - 组合数据类型,如果局部变量未真实创建,则是全局变量
lambda函数
lambda函数返回函数名作为结果
- lambda函数是一种匿名函数,即没有名字的函数
- 使用
lambda
保留字定义,函数名是返回结果 - lambda函数用于定义简单的、能够在一行内表示的函数
<函数名> = lambda<参数>:<表达式>
等价于
def <函数名>(<参数>,):
<函数体>
return <返回值>
例:
>>> f = lambda x,y : x + y
>>> f(10,15)
25
没有参数
>>> f = lambda : 'lambda函数'
>>> print(f())
lambda函数
谨慎使用lambda函数
- lambda函数主要用作一些特定函数或方法的函数
- lambda函数有一些固定使用方式,建议逐步掌握
- 一般情况,建议使用
def
定义普通函数
实例7:七段数码管绘制
问题分析:
通过七段数码管显示数字和字母。
turtle绘图体系→七段数码管
效果
实例讲解
基本思路
- 绘制单个数字对应的数码管
- 获得一串数字,绘制对应的数码管
- 获得当前系统时间,绘制真实的时间
步骤1:绘制单个数码管
- 由7个基本线条组成
- 可以有固定顺序
-
不同数字显示不同的线条
import turtle
def drawLine(draw): #绘制单段数码管
turtle.pendown() if draw else turtle.penup() #通过draw控制是真实绘制还是只是飞过去
turtle.fd(40)
turtle.right(90)
def drawDigit(digit): #根据数字绘制七段数码管
drawLine(True) if digit in [2,3,4,5,6,8,9] else drawLine(False)
drawLine(True) if digit in [0,1,3,4,5,6,7,8,9] else drawLine(False)
drawLine(True) if digit in [0,2,3,5,6,8,9] else drawLine(False)
drawLine(True) if digit in [0,2,6,8] else drawLine(False)
turtle.left(90)
drawLine(True) if digit in [0,4,5,6,8,9] else drawLine(False)
drawLine(True) if digit in [0,2,3,5,6,7,8,9] else drawLine(False)
drawLine(True) if digit in [0,1,2,3,4,7,8,9] else drawLine(False)
turtle.left(180)
turtle.penup() #为绘制后续数字确定位置
turtle.fd(20) #为绘制后续数字确定位置
步骤2:获取一段数字,绘制多个数码管
def drawDate(date): #获得想要输出的数字
for i in date:
drawDigit(eval(i))
def main():
turtle.setup(800, 350, 200, 200)
turtle.penup()
turtle.fd(-300)
turtle.pensize(5)
drawDate('20181010')
turtle.hideturtle()
turtle.done()
main()
步骤3:获得当前系统时间,绘制对应的数码管
- 使用time库获得当前系统时间
- 增加年月日标记
- 年月日颜色不同
import turtle, time
def drawGap(): #绘制数码管间隔
turtle.penup()
turtle.fd(5)
def drawLine(draw): #绘制单段数码管
drawGap()
turtle.pendown() if draw else turtle.penup() #通过draw控制是真实绘制还是只是飞过去
turtle.fd(40)
drawGap()
turtle.right(90)
def drawDigit(digit): #根据数字绘制七段数码管
...(略)
def drawDate(date): #date为日期,格式为'%Y-%m=%d+'
turtle.pencolor('red')
for i in date:
if i == '-':
turtle.write('年', font=('Arial', 18, 'normal'))
turtle.pencolor('green')
turtle.fd(40)
elif i == '=':
turtle.write('月', font=('Arial', 18, 'normal'))
turtle.pencolor('blue')
turtle.fd(40)
elif i == '+':
turtle.write('日', font=('Arial', 18, 'normal'))
else:
drawDigit(eval(i))
def main():
turtle.setup(800, 350, 200, 200)
turtle.penup()
turtle.fd(-300)
turtle.pensize(5)
drawDate(time.strftime('%Y-%m=%d+',time.gtime()))
turtle.hideturtle()
turtle.done()
main()
举一反三
理解方法思维
- 模块化思维:确定模块接口,封装功能
- 规则化思维:抽象过程为规则,计算机自动执行
- 化繁为简:将大功能变为小功能组合,分而治之
应用扩展
- 带小数点的七段数码管
- 带刷新时间的倒计时效果
- 绘制更多形式的数码管
代码复用与函数递归
代码复用与模块化设设计
将代码当成资源进行抽象
- 代码资源化:程序代码是一种用来表达计算的“资源”
- 代码抽象化:使用函数等方法对代码赋予更高级别的定义
- 代码复用:同一份代码在需要时进行复用
函数和对象是代码复用的两种主要形式 - 函数:将代码命名,在代码层面建立初步抽象
- 对象:属性和方法
.()
分而治之
- 通过函数或对象封装将程序划分为模块及模块间的表达
- 具体包括:主程序、子程序和子程序间关系
紧耦合、松耦合
- 紧耦合:两个部分之间交流很多,无法独立存在
- 松耦合:两个部分之间交流很少,可以独立存在
- 模块内部紧耦合、模块之间松耦合
函数递归的理解
在函数定义中,调用函数自身的方式
两个关键特性
- 链条:计算过程存在递归链条
- 基例:存在一个或多个不需要再次递归的实例
类似数学归纳法
函数递归的调用过程
以递归方式计算n!:
def fact(n):
if n == 0:
return 1
else:
return n*fact(n-1)
函数+分支语句
- 递归本身是一个函数,需要函数定义方式描述
- 函数内部,采用分支语句对输入参数进行判断
- 基例和链条,分别编写对应代码
函数递归实例解析
字符串反转
将字符串反转后输出s[::-1]
def rvs(s):
if s == '':
return s
else:
return rvs(s[1:])+s[0]
斐波那契数列
n=1,2时,F(n)=1;n>2时,F(n)=F(n-1)+F(n-2)
def f(n):
if n == 1 or n == 2:
return 1
else:
return f(n-1)+f(n-2)
汉诺塔问题
需要搬运过程和次数
count = 0
def hanoi(n, src, dst, mid):
global count
if n == 1:
print("{}:{}->{}".format(1,src,dst))
count += 1
else:
hanoi(n-1, src, mid, dst)
print("{}:{}->{}".format(n,src,dst))
count += 1
hanoi(n-1, mid, dst, src)
PyInstaller库的使用
将.py源代码转换成无需源代码的可执行文件(Windows下为.exe文件)
PyInstaller是第三方库,安装第三方库需要pip工具
安装
cmd命令行
pip install pyinstaller
PyInstaller库使用说明
cmd命令行
pyinstaller -F <文件名.py>
打包后build和pycache文件夹可删除,dist文件夹里有exe文件。
常用参数
参数 | 描述 |
---|---|
-h |
查看帮助 |
--clean |
清理打包过程中的临时文件 |
-D ,--onedir |
默认值,生成dist文件夹 |
-F ,--onefile |
在dist文件夹中只生成独立的打包文件 |
-i <图标文件名.ico> |
指定打包程序使用图标文件 |
实例8:科赫雪花小包裹
问题分析
分形几何
- 一种迭代的几何图形,广泛存在于自然界中
科赫曲线,也叫雪花曲线
一条直线,取中间1/3长度,作一个60度角,一阶
实例讲解
#KochDrawV1.py
import turtle
def koch(size, n):
if n == 0:
turtle.fd(size)
else:
for angle in [0, 60, -120, 60]:
turtle.left(angle)
koch(size/3, n-1)
def main():
turtle.setup(800, 400)
turtle.penup()
turtle.goto(-300, -50)
turtle.pendown()
turtle.pensize(2)
koch(600, 3)
turtle.hideturtle()
main()
绘制雪花,main()
修改为
def main():
turtle.setup(600, 600)
turtle.penup()
turtle.goto(-200, 100)
turtle.pendown()
turtle.pensize(2)
level = 3
koch(400, level)
turtle.right(120)
koch(400, level)
turtle.right(120)
koch(400, level)
turtle.hideturtle()
main()
举一反三
绘制条件的扩展
- 修改分形几何绘制阶数
- 修改科赫曲线的基本定义及旋转角度
- 修改科赫雪花的基础框架图像
分形几何千千万