- 目标
- 01. 函数的快速体验
- 02. 函数基本使用
- 解释器知道这里定义了一个函数
- 只有在调用函数时,之前定义的函数才会被执行
- 函数执行完成之后,会重新回到之前的程序中,继续执行后续的代码
- 05. 函数的嵌套调用
- 06. 使用模块中的函数
- 02. 可变和不可变类型
- 03. 局部变量和全局变量
- 04.函数参数和返回值的作用
- 05. 函数的返回值 进阶
- 06. 函数的参数 进阶
- 赋值语句
- num = num + num
- num_list.extend(num_list) 由于是调用方法,所以不会修改变量的引用
- 函数执行结束后,外部数据同样会发生变化
- 3 多值参数(知道)
- 需要将一个元组变量/字典变量传递给函数对应的参数
- 会把 gl_nums 和 gl_xiaoming 作为元组传递给 args
- demo(gl_nums, gl_xiaoming)
- 假设 sum_numbers 能够完成 num - 1 的累加
- 函数内部的核心算法就是 两个数字的相加
目标
- 函数的快速体验
- 函数的基本使用
- 函数的参数
- 函数的返回值
- 函数的嵌套调用
-
01. 函数的快速体验
1.1 快速体验
所谓函数,就是把具有独立功能的代码块组织为一个小模块,在需要的时候调用
- 函数的使用包含两个步骤:
- 定义函数 —— 封装 独立的功能
- 调用函数 —— 享受 封装 的成果
函数的作用,在开发程序时,使用函数可以提高编写的效率以及代码的重用
02. 函数基本使用
2.1 函数的定义
定义函数的格式如下:
def 函数名():函数封装的代码……
- def是英文define的缩写
- 函数名称应该能够表达函数封装代码的功能,方便后续的调用
- 函数名称的命名应该符合标识符的命名规则
- \1. 编写一个打招呼 say_hello 的函数,封装三行打招呼的代码
- \2. 在函数下方调用打招呼的代码 ```python name = “小明”
解释器知道这里定义了一个函数
def say_hello(): print(“hello 1”) print(“hello 2”) print(“hello 3”)
print(name)
只有在调用函数时,之前定义的函数才会被执行
函数执行完成之后,会重新回到之前的程序中,继续执行后续的代码
say_hello()
print(name)
用**单步执行 F8 和 F7**观察以下代码的执行过程- 定义好函数之后,只表示这个函数封装了一段代码而已- 如果不主动调用函数,函数是不会主动执行的<a name="aQGr0"></a>#### 思考- 能否将**函数调用**放在**函数定义**的上方?- 不能!- 因为在 **使用函数名** 调用函数之前,必须要保证 Python 已经知道函数的存在- 否则控制台会提示 NameError: name 'say_hello' is not defined (**名称错误:say_hello 这个名字没有被定义**)<a name="C9hX3"></a>### 2.4 函数的文档注释- 在开发中,如果希望给函数添加注释,应该在 **定义函数** 的下方,使用 **连续的三对引号**- 在 **连续的三对引号** 之间编写对函数的说明文字- 在 **函数调用** 位置,使用快捷键 CTRL + Q 可以查看函数的说明信息注意:因为**函数体相对比较独立**,**函数定义的上方**,应该和其他代码(包括注释)保留**两个空行**<a name="OwRKG"></a>## 03. 函数的参数**演练需求**1. 开发一个 sum_2_num 的函数1. 函数能够实现 **两个数字的求和** 功能演练代码如下:```pythondef sum_2_num():num1 = 10num2 = 20result = num1 + num2print("%d + %d = %d" % (num1, num2, result))sum_2_num()
思考一下存在什么问题
函数只能处理固定数值的相加
如何解决?
如果能够把需要计算的数字,在调用函数时,传递到函数内部就好了!
3.1 函数参数的使用
在函数名的后面的小括号内部填写 参数
多个参数之间使用 , 分隔 ```python def sum_2_num(num1, num2):
result = num1 + num2
print(“%d + %d = %d” % (num1, num2, result))
sum_2_num(50, 20)
<a name="ZDimm"></a>### 3.2 参数的作用- **函数**,把**具有独立功能的代码块**组织为一个小模块,在需要的时候**调用**- 函数的参数,增加函数的通用性,针对相同的数据处理逻辑,能够适应更多的数据1. 在函数 **内部**,把参数当做 **变量** 使用,进行需要的数据处理1. 函数调用时,按照函数定义的**参数顺序**,把 **希望在函数内部处理的数据**,**通过参数** 传递<a name="pmi53"></a>### 3.3 形参和实参- **形参**:**定义** 函数时,小括号中的参数,是用来接收参数用的,在函数内部 **作为变量使用**- **实参**:**调用** 函数时,小括号中的参数,是用来把数据传递到 **函数内部** 用的<a name="ntdbA"></a>## 04. 函数的返回值- 在程序开发中,有时候,会希望 **一个函数执行结束后,告诉调用者一个结果**,以便调用者针对具体的结果做后续的处理- **返回值** 是函数 **完成工作**后,**最后** 给调用者的 **一个结果**- 在函数中使用 return 关键字可以返回结果- 调用函数一方,可以 **使用变量** 来 **接收** 函数的返回结果注意:return表示返回,后续的代码都不会被执行```pythondef sum_2_num(num1, num2):"""对两个数字的求和"""return num1 + num2# 调用函数,并使用 result 变量接收计算结果result = sum_2_num(10, 20)print("计算结果是 %d" % result)
05. 函数的嵌套调用
- 一个函数里面又调用了另外一个函数,这就是函数嵌套调用
如果函数 test2中,调用了另外一个函数test1
- 那么执行到调用 test1 函数时,会先把函数 test1 中的任务都执行完
才会回到 test2 中调用函数 test1 的位置,继续执行后续的代码 ```python def test1():
print(““ 50) print(“test 1”) print(““ 50)
def test2():
print("-" * 50)print("test 2")test1()print("-" * 50)
test2()
<a name="rZtC6"></a>### 函数嵌套的演练 —— 打印分隔线python体会一下工作中**需求是多变**的<br />**需求 1**- 定义一个 print_line 函数能够打印 * 组成的 **一条分隔线**```pythondef print_line(char):print("*" * 50)
需求 2
定义一个函数能够打印 由任意字符组成 的分隔线 ```python def print_line(char):
print(char * 50)
**需求 3**- 定义一个函数能够打印 **任意重复次数** 的分隔线```pythondef print_line(char, times):print(char * times)
print 和 return 是一样的道理,用return的话 需要定义一个变量来接收。
需求 4
- 定义一个函数能够打印 5 行 的分隔线,分隔线要求符合需求 3
提示:工作中针对需求的变化,应该冷静思考,不要轻易修改之前已经完成的,能够正常执行的函数!
def print_line(char, times):print(char * times)def print_lines(char, times):row = 0while row < 5:print_line(char, times)row += 1
06. 使用模块中的函数
模块是 Python 程序架构的一个核心概念
- 模块 就好比是 工具包,要想使用这个工具包中的工具,就需要 导入 import 这个模块
- 每一个以扩展名 py 结尾的 Python 源代码文件都是一个 模块
在模块中定义的 全局变量 、 函数 都是模块能够提供给外界直接使用的工具
6.1 第一个模块体验
步骤
新建lx_分隔线模块.py
- 复制 `lx_打印多条分隔线.py 中的内容,最后一行 print 代码除外
- 增加一个字符串变量
- 新建 lx体验模块.py 文件,并且编写以下代码: ```python import lx分隔线模块
lx分隔线模块.print_line(“-“, 80) print(lx分隔线模块.name)
<a name="AJvXg"></a>#### 体验小结- 可以 **在一个 Python 文件** 中 **定义 变量 或者 函数**- 然后在 **另外一个文件中** 使用 import 导入这个模块- 导入之后,就可以使用 模块名.变量 / 模块名.函数 的方式,使用这个模块中定义的变量或者函数**模块**可以让**曾经编写过的代码**方便的被**复用**!**函数进阶**<a name="p1SG7"></a>## 目标- 变量的引用- 可变和不可变类型- 局部变量和全局变量- 匿名函数<a name="aYrKU"></a>## 01. 变量的引用- 变量 和 数据 都是保存在 **内存** 中的- 在 Python 中 **函数 的 参数传递** 以及 **返回值** 都是靠 **引用** 传递的<a name="UbqrE"></a>### 1.1 引用的概念在Python中- **变量** 和 **数据** 是分开存储的- **数据** 保存在内存中的一个位置- **变量** 中保存着数据在内存中的地址- **变量** 中 **记录数据的地址**,就叫做 **引用**- 使用 id() 函数可以查看变量中保存数据所在的 **内存地址**注意:如果变量已经被定义,当给一个变量赋值的时候,本质上是**修改了数据的引用**- 变量 **不再** 对之前的数据引用- 变量 **改为** 对新赋值的数据引用<a name="UZJl4"></a>### 1.2 变量引用 的示例在Python中,变量的名字类似于**便签纸**贴在**数据**上- 定义一个整数变量 a,并且赋值为 1- - 将变量 a 赋值为 2- - 定义一个整数变量 b,并且将变量 a 的值赋值给 b- <a name="GgLa8"></a>### 1.3 函数的参数和返回值的传递在Python中,函数的**实参**/**返回值**都是是靠**引用**来传递来的```pythondef test(num):print("-" * 50)print("%d 在函数内的内存地址是 %x" % (num, id(num)))result = 100print("返回值 %d 在内存中的地址是 %x" % (result, id(result)))print("-" * 50)return resulta = 10print("调用函数前 内存地址是 %x" % id(a))r = test(a)print("调用函数后 实参内存地址是 %x" % id(a))print("调用函数后 返回值内存地址是 %x" % id(r))
02. 可变和不可变类型
- 不可变类型,内存中的数据不允许被修改:
- 数字类型 int, bool, float, complex, long(2.x)
- 字符串 str
- 元组 tuple
可变类型,内存中的数据可以被修改:
- 列表 list
- 字典 dict
注意
- 局部变量 是在 函数内部 定义的变量,只能在函数内部使用
- 全局变量 是在 函数外部定义 的变量(没有定义在某一个函数内),所有函数 内部 都可以使用这个变量
提示:在其他的开发语言中,大多不推荐使用全局变量—— 可变范围太大,导致程序不好维护!
3.1 局部变量
- 局部变量 是在 函数内部 定义的变量,只能在函数内部使用
- 函数执行结束后,函数内部的局部变量,会被系统回收
不同的函数,可以定义相同的名字的局部变量,但是 彼此之间 不会产生影响
局部变量的作用
在函数内部使用,临时 保存 函数内部需要使用的数据 ```python def demo1():
num = 10
print(num)
num = 20
print(“修改后 %d” % num)
def demo2():
num = 100print(num)
demo1() demo2()
print(“over”)
**注意**:函数执行时,**需要处理变量时**会:1. **首先** 查找 **函数内部** 是否存在 **指定名称 的局部变量**,**如果有,直接使用**1. 如果没有,查找 **函数外部** 是否存在 **指定名称 的全局变量**,**如果有,直接使用**1. 如果还没有,程序报错!<a name="asAtP"></a>#### 1) 函数不能直接修改 全局变量的引用- **全局变量** 是在 **函数外部定义** 的变量(没有定义在某一个函数内),**所有函数** 内部 **都可以使用这个变量**提示:在其他的开发语言中,大多**不推荐使用全局变量**—— 可变范围太大,导致程序不好维护!- 在函数内部,可以 **通过全局变量的引用获取对应的数据**- 但是,**不允许直接修改全局变量的引用** —— 使用赋值语句修改全局变量的值```pythonnum = 10def demo1():print("demo1" + "-" * 50)# 只是定义了一个局部变量,不会修改到全局变量,只是变量名相同而已num = 100print(num)def demo2():print("demo2" + "-" * 50)print(num)demo1()demo2()print("over")
注意:只是在函数内部定义了一个局部变量而已,只是变量名相同 —— 在函数内部不能直接修改全局变量的值
2) 在函数内部修改全局变量的值
- 如果在函数中需要修改全局变量,需要使用 global 进行声明 ```python num = 10
def demo1():
print("demo1" + "-" * 50)# global 关键字,告诉 Python 解释器 num 是一个全局变量global num# 只是定义了一个局部变量,不会修改到全局变量,只是变量名相同而已num = 100print(num)
def demo2():
print("demo2" + "-" * 50)print(num)
demo1() demo2()
print(“over”)
<a name="tgThW"></a>#### 3) 全局变量定义的位置- 为了保证所有的函数都能够正确使用到全局变量,应该 **将全局变量定义在其他函数的上方**```pythona = 10def demo():print("%d" % a)print("%d" % b)print("%d" % c)b = 20demo()c = 30
注意
由于全局变量 c,是在调用函数之后,才定义的,在执行函数时,变量还没有定义,所以程序会报错!
4) 全局变量命名的建议
为了避免局部变量和全局变量出现混淆,在定义全局变量时,有些公司会有一些开发要求,例如:
- 全局变量名前应该增加 g 或者 gl 的前缀
04.函数参数和返回值的作用
函数根据 有没有参数 以及 有没有返回值,可以 相互组合,一共有 4 种 组合形式
- 无参数,无返回值
- 无参数,有返回值
- 有参数,无返回值
- 有参数,有返回值

定义函数时,是否接收参数,或者是否返回结果,是根据 实际的功能需求 来决定的!
- 如果函数 内部处理的数据不确定,就可以将外界的数据以参数传递到函数内部
如果希望一个函数 执行完成后,向外界汇报执行结果,就可以增加函数的返回值
1.1 无参数,无返回值
此类函数,不接收参数,也没有返回值,应用场景如下:
只是单纯地做一件事情,例如 显示菜单
- 在函数内部 针对全局变量进行操作,例如:新建名片,最终结果 记录在全局变量 中
注意:
- 如果全局变量的数据类型是一个 可变类型,在函数内部可以使用 方法 修改全局变量的内容 —— 变量的引用不会改变
-
1.2 无参数,有返回值
此类函数,不接收参数,但是有返回值,应用场景如下:
采集数据,例如 温度计,返回结果就是当前的温度,而不需要传递任何的参数
1.3 有参数,无返回值
此类函数,接收参数,没有返回值,应用场景如下:
函数内部的代码保持不变,针对 不同的参数 处理 不同的数据
-
1.4 有参数,有返回值
此类函数,接收参数,同时有返回值,应用场景如下:
函数内部的代码保持不变,针对 不同的参数 处理 不同的数据,并且 返回期望的处理结果
例如名片管理系统使用字典默认值和提示信息提示用户输入内容
在程序开发中,有时候,会希望 一个函数执行结束后,告诉调用者一个结果,以便调用者针对具体的结果做后续的处理
- 返回值 是函数 完成工作后,最后 给调用者的 一个结果
- 在函数中使用 return 关键字可以返回结果
- 调用函数一方,可以 使用变量 来 接收 函数的返回结果
示例 —— 温度和湿度测量
- 假设要开发一个函数能够同时返回当前的温度和湿度
先完成返回温度的功能如下: ```python def measure(): “””返回当前的温度”””
print(“开始测量…”) temp = 39 print(“测量结束…”)
return temp
result = measure() print(result)
- 在利用 **元组** 在返回温度的同时,也能够返回 **湿度**- 改造如下:```pythondef measure():"""返回当前的温度"""print("开始测量...")temp = 39wetness = 10print("测量结束...")return (temp, wetness)
提示:如果一个函数返回的是元组,括号可以省略
技巧
- 在 Python 中,可以 将一个元组 使用 赋值语句 同时赋值给 多个变量
- 注意:变量的数量需要和元组中的元素数量保持一致
- result = temp, wetness = measure()
面试题 —— 交换两个数字
题目要求
- 有两个整数变量 a = 6, b = 100
- 不使用其他变量,交换两个变量的值
解法 1 —— 使用其他变量
# 解法 1 - 使用临时变量c = bb = aa = c
解法 2 —— 不使用临时变量
# 解法 2 - 不使用临时变量a = a + bb = a - ba = a - b
解法 3 —— Python 专有,利用元组
a, b = b, a
06. 函数的参数 进阶
1. 不可变和可变的参数
问题 1:在函数内部,针对参数使用 赋值语句,会不会影响调用函数时传递的 实参变量? —— 不会!
无论传递的参数是可变还是不可变
gl_num = 99 gl_list = [4, 5, 6] demo(gl_num, gl_list) print(gl_num) print(gl_list)
问题 2:如果传递的参数是 **可变类型**,在函数内部,使用 **方法** 修改了数据的内容,**同样会影响到外部的数据**```pythondef mutable(num_list):# num_list = [1, 2, 3]num_list.extend([1, 2, 3])print(num_list)gl_list = [6, 7, 8]mutable(gl_list)print(gl_list)
面试题 —— +=
在 python 中,列表变量调用 += 本质上是在执行列表变量的 extend 方法,不会修改变量的引用 ```python def demo(num, num_list):
print(“函数内部代码”)
num = num + num
num += num
num_list.extend(num_list) 由于是调用方法,所以不会修改变量的引用
函数执行结束后,外部数据同样会发生变化
num_list += num_list
print(num) print(num_list) print(“函数代码完成”)
gl_num = 9 gl_list = [1, 2, 3] demo(gl_num, gl_list) print(gl_num) print(gl_list)
<a name="WJhgV"></a>### 2 缺省参数- 定义函数时,可以给 **某个参数** 指定一个**默认值**,具有默认值的参数就叫做 **缺省参数**- 调用函数时,如果没有传入 **缺省参数** 的值,则在函数内部使用定义函数时指定的 **参数默认值**- 函数的缺省参数,**将常见的值设置为参数的缺省值**,从而 **简化函数的调用**- 例如:对列表排序的方法```pythongl_num_list = [6, 3, 9]# 默认就是升序排序,因为这种应用需求更多gl_num_list.sort()print(gl_num_list)# 只有当需要降序排序时,才需要传递 `reverse` 参数gl_num_list.sort(reverse=True)print(gl_num_list)
指定函数的缺省参数
在参数后使用赋值语句,可以指定参数的缺省值
def print_info(name, gender=True):gender_text = "男生"if not gender:gender_text = "女生"print("%s 是 %s" % (name, gender_text))
提示
- 缺省参数,需要使用 最常见的值 作为默认值!
- 如果一个参数的值 不能确定,则不应该设置默认值,具体的数值在调用函数时,由外界传递!
- 在调用参数时,比如 print_info()时候里面填的bool值是什么它就是什么(小明,False)它就是女生,如果(小明)后面不带False,它就默认是男生。
缺省参数的注意事项
1) 缺省参数的定义位置
- 必须保证带有默认值的缺省参数在参数列表末尾
所以,以下定义是错误的!
def print_info(name, gender=True, title):
2) 调用带有多个缺省参数的函数
在 调用函数时,如果有 多个缺省参数,需要指定参数名,这样解释器才能够知道参数的对应关系!
3 多值参数(知道)
定义支持多值参数的函数
有时可能需要 一个函数 能够处理的参数 个数 是不确定的,这个时候,就可以使用 多值参数
- python中有两种多值参数:
- 参数名前增加 一个* 可以接收 元组
- 参数名前增加 两个* 可以接收 字典
- 一般在给多值参数命名时,习惯使用以下两个名字
- args —— 存放 元组 参数,前面有一个
- kwargs —— 存放 字典* 参数,前面有两个
- args 是 arguments 的缩写,有变量的含义
kw 是 keyword 的缩写,kwargs 可以记忆 键值对参数 ```python def demo(num, args, *kwargs):
print(num) print(args) print(kwargs)
demo(1, 2, 3, 4, 5, name=”小明”, age=18, gender=True)
提示:**多值参数** 的应用会经常出现在网络上一些大牛开发的框架中,知道多值参数,**有利于我们能够读懂大牛的代码**<a name="iP2nb"></a>#### 多值参数案例 —— 计算任意多个数字的和**需求**1. 定义一个函数 sum_numbers,可以接收的 **任意多个整数**1. 功能要求:将传递的 **所有数字累加** 并且返回累加结果```pythondef sum_numbers(*args):num = 0# 遍历 args 元组顺序求和for n in args:num += nreturn numprint(sum_numbers(1, 2, 3))
元组和字典的拆包(知道)
- 在调用带有多值参数的函数时,如果希望:
- 将一个 元组变量,直接传递给 args
- 将一个 字典变量,直接传递给 kwargs
就可以使用拆包,简化参数的传递,拆包的方式是:
- 在 元组变量前,增加 一个*
在 字典变量前,增加 两个* ```python def demo(args, *kwargs):
print(args) print(kwargs)
需要将一个元组变量/字典变量传递给函数对应的参数
gl_nums = (1, 2, 3) gl_xiaoming = {“name”: “小明”, “age”: 18}
会把 gl_nums 和 gl_xiaoming 作为元组传递给 args
demo(gl_nums, gl_xiaoming)
demo(gl_nums, *gl_xiaoming)
我希望他传递给元祖*args,我就标上一个* 我希望他传递给字典**kwargs,我就加上两个*<a name="oW86Q"></a>## 07. 函数的递归函数调用自身的 **编程技巧** 称为递归<a name="mPWUx"></a>### 1 递归函数的特点**特点**- 一个函数内部调用自己- 函数内部可以调用其他函数,当然在函数内部也可以调用自己**代码特点**1. 函数内部的 **代码** 是相同的,只是针对 **参数** 不同,**处理的结果不同**1. 当参数满足一个条件时,函数不再执行- **这个非常重要**,通常被称为递归的出口,否则 **会出现死循环**!示例代码```pythondef sum_numbers(num):print(num)# 递归的出口很重要,否则会出现死循环if num == 1:returnsum_numbers(num - 1)sum_numbers(3)
2 递归案例 —— 计算数字累加
需求
- 定义一个函数 sum_numbers
- 能够接收一个 num 的整数参数
计算 1 + 2 + … num 的结果 ```python def sum_numbers(num):
if num == 1:
return 1
假设 sum_numbers 能够完成 num - 1 的累加
temp = sum_numbers(num - 1)
函数内部的核心算法就是 两个数字的相加
return num + temp
print(sum_numbers(2))
<br />提示:递归是一个 **编程技巧**,初次接触递归会感觉有些吃力!在处理 **不确定的循环条件时**,格外的有用,例如:**遍历整个文件目录的结构**<a name="FH101"></a>## 08.匿名函数python 使用 lambda 来创建匿名函数。<br />lambda这个名称来自于LISP,而LISP则是从lambda calculus(一种符号逻辑形式)取这个名称的。 在Python中,lambda作为一个关键字,作为引入表达式的语法。想比较def函数,lambda是单一的表达式,而不是语句块!<br />**所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。**- lambda 只是一个表达式,函数体比 def 简单很多。- lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。- lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。- 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。<a name="EnwaY"></a>##### 语法lambda 函数的语法只包含一个语句,如下:```pythonlambda [arg1 [,arg2,.....argn]]:expression# lambda 参数列表:return [表达式] 变量# 由于lambda返回的是函数对象(构建的是一个函数对象),所以需要定义一个变量去接收
# 可写函数说明sum = lambda arg1, arg2: arg1 + arg2;# 调用sum函数print ("相加后的值为 : ", sum( 10, 20 )) # 相加后的值为 : 30print ("相加后的值为 : ", sum( 20, 20 )) # 相加后的值为 : 40
def test(a,b,func):result = func(a,b)return resultnum = test(11,22,lambda x,y:x+y)print(num)
匿名函数优点
- 使用Python写一些脚本时,使用lambda可以省去定义函数的过程,让代码更加精简。
- 对于一些抽象的,不会被别的地方再重复使用的函数,有时候函数起个名字也是个难题,使用lambda不需要考虑命名的问题
- 使用lambda在某些时候然后代码更容易理解
