【第5周笔记】函数和代码复用
课程笔记和选择题
notes
函数的定义
函数是一段代码的表示
函数是一段具有特定功能的、可重用的语句组
函数是一种功能的抽象,一般函数表达特定功能
两个作用:降低编程难度和代码复用
def <函数名>(<参数(0 个或多个)>) : # 函数可以有参数,也可以没有,但必须保留括号
<函数体>
return <返回值>
函数定义时,所指定的参数是一种占位符
函数定义后,如果不经过调用,不会被执行
函数定义时,参数是输入、函数体是处理、结果是输出(IPO)
函数的调用
调用是运行函数代码的方式
调用时要给出实际参数
实际参数替换定义中的参数
函数调用后得到返回值

可选参数传递
函数定义时可以为某些参数指定默认值,构成可选参数
def <函数名>(<非可选参数>, <可选参数>) :
<函数体>
return <返回值>
# 计算 n!//m
def fact(n, m=1) : # m=1 为可选参数
s = 1
for i in range(1, n=1) :
s *= i
return s//m
# 举例
>>> fact(10)
# 输出
3628800
# 举例
>>> fact(10,5)
# 输出
725760
可变参数传递
函数定义时可以设计可变数量参数,既不确定参数总数量
def <函数名>(<参数>, *b) :
<函数体>
return <返回值>
# 计算 n! 乘数
def fact(n, *b) : # *b 为可变参数
s = 1
for i in range(1, n+1) :
s *= i
for item in b :
s *= item
return s
# 举例
>>> fact(10,3)
# 输出
10886400
# 举例
>>> fact(10,3,5,8)
# 输出
435456000
参数传递的两种方式
函数调用时,参数可以按照位置或名称方式传递
def fact(n, m=1) :
s = 1
for i in range(1, n+1) :
s *= i
return s//m
# 举例
>>> fact(10,5) # 位置传递
# 输出
725760
# 举例
>>> fact(m=5,n=10) # 名称传递
# 输出
725760
函数的返回值
函数可以返回 0 个或多个结果
return 保留字用来传递返回值
函数可以有返回值,也可以没有,可以有 return,也可以没有
return 可以传递 0 个返回值,也可以传递任意多个返回值
函数调用时,参数可以按照位置或名称方式传递
局部变量和全局变量
局部变量和全局变量是不同变量
局部变量是函数内部的占位符,与全局变量可能重名但不同
n, s = 10, 100
def fact(n) :
s = 1 # fact() 函数中 s 是局部变量,与全局变量 s 不同
for i in range(11 n+1) :
s *= i
return s # 此处局部变量 s 是 3628800
print(fact(n), s) # 此处全局变量 s 是 100
函数运算结束后,局部变量被释放
可以使用 global 保留字在函数内部使用全局变量
n, s = 10, 100
def fact(n) :
global s # fact() 函数中使用 global 保留字声明:此处 s 是全局变量 s
for i in range(1, n+1) :
s *= i
return s # 此处 s 指全局变量 s,值为 362880000
print(fact(n), s) # 此处全局变量 s 被函数修改,值同为 362880000
局部变量为组合数据类型且未创建,等同于全局变量
ls = ["F", "f"] # 通过使用 [] 真实创建了一个全局变量列表 ls
def func(a) :
ls.append(a) # 此处 ls 是列表类型,未真实创建,则等同于全局变量
return
func("C") # 全局变量 ls 被修改
print(ls) # 运行结果为:['F', 'f', 'C']
ls = ["F", "f"] # 通过使用 [] 真实创建了一个全局变量列表 ls
def func(a) :
ls = [] # 此处 ls 是列表类型,真实创建,ls 是局部变量
ls.append(a)
return
func("C") # 局部变量 ls 被修改
print(ls) # 运行结果为:['F', 'f']
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 定义的普通函数
代码复用
把代码当成资源进行抽象
代码资源化:程序代码是一种用来表达计算的"资源"
代码抽象化:使用函数等方法对代码赋予更高级别的定义
代码复用:同一份代码在需要时可以被重复使用
“函数”和“对象”是代码复用的两种主要形式
- 函数:将代码命名,在代码层建立了初步抽象
- 对象:属性和方法,
<a>.<b> 和 <a>.<b>()
,在函数之上再次组织进行抽象
模块化设计
通过函数或对象封装将程序划分为模块及模块间的表达
具体包括:主程序、子程序和子程序间关系
分而治之:一种分而治之、分层抽象、体系化的设计思想
紧耦合:两个部分之间交流很多,无法独立存在
松耦合:两个部分之间交流较少,可以独立存在
模块内部紧耦合、模块之间松耦
递归的定义
函数定义中调用函数自身的方式
两个关键特征
链条:计算过程存在递归链条
基例:存在一个或多个不需要再次递归的基例
类似数学归纳法
- 证明当 n 取第一个值 $n_0$ 时命题成立
- 假设当 $n_k$ 时命题成立,证明当 $n=n_{k+1}$ 时命题也成立
递归是数学归纳法思维的编程体现
递归的实现
函数 + 分支语句
递归本身是一个函数,需要函数定义方式描述
函数内部,采用分支语句对输入参数进行判断
基例和链条,分别编写对应代码
函数递归实例解析
字符串反转
将字符串 s 反转后输出,即 s[::-1]
def rvs(s) :
if s == "" :
return s
else :
return rvs(s[1:])+s[0]
斐波那契数列
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源代码转换成无需源代码的可执行文件
PyInstaller 库是第三方库
第三方库:使用前需要额外安装
安装第三方库需要使用 pip 工具
pip install pyinstaller
# 简单的使用
pyinstaller -F <文件名.py>
# 使用举例
pyinstaller -i curve.ico -F SevenDigitsDrawV2.py
PyInstaller 库常用参数
参数 | 描述 |
---|---|
-h | 查看帮助 |
–clean | 清理打包过程中的临时文件 |
-D, –onedir | 默认值,生成 dist 文件夹 |
-F, –onefile | 在 dist 文件夹中生成独立的打包文件 |
-i <图标文件名.ico> | 指定打包程序使用的图标(icon)文件 |
测验 5
以下选项不是函数作用的是:A
A. 提高代码执行速度
B. 降低编程复杂度
C. 增强代码可读性
D. 复用代码
- 函数不能直接提高代码执行速度。
- 下列程序的输出结果为:C
def f(a,b) :
a=4
return a+b
def main() :
a=5
b=6
print(f(a,b),a+b)
main()
A. 11 11
B. 11 10
C. 10 11
D. 10 10
- 这里没有全局变量,都是函数局部变量的运算。
- 以下关于 Python 函数说法错误的是:B
def func(a,b) :
c=a**2+b
b=a
return c
a=10
b=100
c=func(a,b)+a
A. 执行该函数后,变量 a 的值为 10
B. 执行该函数后,变量 c 的值为 200
C. 执行该函数后,变量 b 的值为 100
D. 该函数名称为 func
- a, b 为全局变量,请在 IDLE 中执行代码观察结果。
以下关于函数调用描述正确的是:D
A. 函数和调用只能发生在同一个文件中
B. 函数在调用前不需要定义,拿来即用就好
C. Python 内置函数调用前需要引用相应的库
D. 自定义函数调用前必须定义
函数调用前必须已经存在函数定义,否则无法执行。
Python 内置函数直接使用,不需要引用任何模块。
以下关于模块化设计描述错误的是:D
A. 模块间关系尽可能简单,模块之间耦合度低
B. 高耦合度的特点是复用较为困难
C. 应尽可能合理划分功能块,功能块内部耦合度高
D. 应尽可能合理划分功能块,功能块内部耦合度低
- 模块内高耦合、模块间低耦合。
以下对递归描述错误的是:B
A. 一定要有基例
B. 执行效率高
C. 书写简单
D. 递归程序都可以有非递归编写方法
递归不提高程序执行效率。
任何递归程序都可以通过堆栈或队列变成非递归程序(这是程序的高级应用)。
以下关于函数说法错误的是:B
A. 函数通过函数名来调用
B. 对函数的使用必须了解其内部实现原理
C. 函数是一段具有特定功能的、可重用的语句组
D. 函数可以看做是一段具有名字的子程序
- 调用函数不需要知道函数内部实现原理,只需要知道调用方法(即接口)即可。
哪个选项对于函数的定义是错误的?C
A. def vfunc(a,b):
B. def vfunc(a,*b):
C. def vfunc(*a,b):
D. def vfunc(a,b=2):
- def vfunc(*a, b) 是错误的定义:*a表示可变参数,可变参数只能放在函数参数的最后。
关于 return 语句,以下选项描述正确的是:A
A. 函数可以没有return语句
B. 函数必须有一个return语句
C. return只能返回一个值
D. 函数中最多只有一个return语句
- 函数可以包含 0 个或多个 return 语句
以下关于递归函数基例的说法错误的是:A
A. 每个递归函数都只能有一个基例
B. 递归函数的基例决定递归的深度
C. 递归函数的基例不再进行递归
D. 递归函数必须有基例
- 每个递归函数至少存在一个基例,但可能存在多个基例。