【第5周笔记】函数和代码复用

2022-04-11
2022-04-11
7 min read
Hits

  课程笔记和选择题

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 定义的普通函数

代码复用

  把代码当成资源进行抽象

  代码资源化:程序代码是一种用来表达计算的"资源"

  代码抽象化:使用函数等方法对代码赋予更高级别的定义

  代码复用:同一份代码在需要时可以被重复使用

  “函数”和“对象”是代码复用的两种主要形式

  1. 函数:将代码命名,在代码层建立了初步抽象
  2. 对象:属性和方法,<a>.<b> 和 <a>.<b>(),在函数之上再次组织进行抽象

模块化设计

  通过函数或对象封装将程序划分为模块及模块间的表达

  具体包括:主程序、子程序和子程序间关系

  分而治之:一种分而治之、分层抽象、体系化的设计思想

  紧耦合:两个部分之间交流很多,无法独立存在

  松耦合:两个部分之间交流较少,可以独立存在

  模块内部紧耦合、模块之间松耦

递归的定义

  函数定义中调用函数自身的方式

  两个关键特征

  1. 链条:计算过程存在递归链条

  2. 基例:存在一个或多个不需要再次递归的基例

  3. 类似数学归纳法

    1. 证明当 n 取第一个值 $n_0$ 时命题成立
    2. 假设当 $n_k$ 时命题成立,证明当 $n=n_{k+1}$ 时命题也成立
  4. 递归是数学归纳法思维的编程体现

递归的实现

  函数 + 分支语句

  递归本身是一个函数,需要函数定义方式描述

  函数内部,采用分支语句对输入参数进行判断

  基例和链条,分别编写对应代码

函数递归实例解析

字符串反转

  将字符串 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

  1. 以下选项不是函数作用的是:A

    A. 提高代码执行速度

    B. 降低编程复杂度

    C. 增强代码可读性

    D. 复用代码

  • 函数不能直接提高代码执行速度。
  1. 下列程序的输出结果为: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
  • 这里没有全局变量,都是函数局部变量的运算。
  1. 以下关于 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 中执行代码观察结果。
  1. 以下关于函数调用描述正确的是:D

    A. 函数和调用只能发生在同一个文件中

    B. 函数在调用前不需要定义,拿来即用就好

    C. Python 内置函数调用前需要引用相应的库

    D. 自定义函数调用前必须定义

  • 函数调用前必须已经存在函数定义,否则无法执行。

  • Python 内置函数直接使用,不需要引用任何模块。

  1. 以下关于模块化设计描述错误的是:D

    A. 模块间关系尽可能简单,模块之间耦合度低

    B. 高耦合度的特点是复用较为困难

    C. 应尽可能合理划分功能块,功能块内部耦合度高

    D. 应尽可能合理划分功能块,功能块内部耦合度低

  • 模块内高耦合、模块间低耦合。
  1. 以下对递归描述错误的是:B

    A. 一定要有基例

    B. 执行效率高

    C. 书写简单

    D. 递归程序都可以有非递归编写方法

  • 递归不提高程序执行效率。

  • 任何递归程序都可以通过堆栈或队列变成非递归程序(这是程序的高级应用)。

  1. 以下关于函数说法错误的是:B

    A. 函数通过函数名来调用

    B. 对函数的使用必须了解其内部实现原理

    C. 函数是一段具有特定功能的、可重用的语句组

    D. 函数可以看做是一段具有名字的子程序

  • 调用函数不需要知道函数内部实现原理,只需要知道调用方法(即接口)即可。
  1. 哪个选项对于函数的定义是错误的?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表示可变参数,可变参数只能放在函数参数的最后。
  1. 关于 return 语句,以下选项描述正确的是:A

    A. 函数可以没有return语句

    B. 函数必须有一个return语句

    C. return只能返回一个值

    D. 函数中最多只有一个return语句

  • 函数可以包含 0 个或多个 return 语句
  1. 以下关于递归函数基例的说法错误的是:A

    A. 每个递归函数都只能有一个基例

    B. 递归函数的基例决定递归的深度

    C. 递归函数的基例不再进行递归

    D. 递归函数必须有基例

  • 每个递归函数至少存在一个基例,但可能存在多个基例。
Avatar

Hui.Ke

❤ Cyber Security | Safety is a priority.