通过之前的学习,我们就把容器大概的过了一遍了。接下来就是下一个重点--函数!不过函数的知识点非常多,自习讲起来篇幅就不是一两篇可以说完的了,之后我会不定期更新函数知识,到时候在另外开一个系列。
接下来就来一起学习函数的基础吧~
一、函数的应用场景
如果一个功能需要反复使用有两种解决办法:
1、将实现需要反复使用的代码反复写
2、将实现需要反复使用的代码封装成函数,然后再需要使用这个功能的时候调用函数
二、函数的定义和调用
1、什么是函数
(1) 概念:函数就是实现莫以特定功能代码的封装
(函数就是把实现了某一个功能的所有代码达成一个包,然后再需要这个功能的 时候直接调用这个函数)
(2)分类:根据函数由谁创建可以将函数分为两种:
a.系统函数:由系统创建好的函数,例如:print、input、type、max、min等
b.自定义函数:由程序员自己造的函数
2、定义函数
(1)语法:
def 函数名(形参列表):
函数说明文档
函数体
(2)说明:
a. def → 关键字;固定写法
b. 函数名 → 程序员自己命名
两个要求:是标识符,不能是关键字;
三个规范:见名知义(看到函数名就大概知道函数的功能);
所有字母都小写,如果有多个单词,单词之间用下划线隔开;
c. (): → 固定写法
d. 形参列表 → 以‘变量名1,变量名2,变量名3,…’形式存在,里面的每一个变量 就是一 个形参
形参的作用是将函数外部的数据传递到函数里面
怎么确定形参的数量:看实现函数的功能需不需要额外的数据,需 要几个
e. 函数说明文档 → 本质就是多行注释
f. 函数体 → 结构上,就是和def保持一个缩进的一条或者多条语句,至少一条
逻辑上,函数体就是实现函数功能的所有代码
注意:通过函数体实现函数功能的时候,将形参当成对应的数据来使用
# 案例1:定义一个函数,求5的阶乘
def f_5():
sum1 = 1
for x in range(1,6):
sum1 *= x
print(sum1)
# 案例2:定义一个函数,求任意两个数的和
def sum2(num1,num2):
'''
打印两个数的和
:param num1: 提供第一个数
:param num2: 提供第二个数
:return: None
'''
print(num1 + num2)
# 案例3:定义一个函数,统计字符串中数字字符的个数
def num_char_count(str1):
'''
统计字符串中数字字符的个数
:param str1: 原字符串
:return: None
'''
count = 0
for x in str1:
if x.isdigit():
count += 1
print(count)
#练习1:定义一个函数,将字符串中的第一个字符由小写字母变成大写字母
def capitalize(str1):
'''
将字符串中的第一个字符由小写字母变成大写字母
:param str1: 需要转换的原字符串
:return: None
'''
if str1[0].islower():
print(str1[0].upper()+str1[1:])
else:
print(str1)
# 练习2:定义一个函数统计字符串中数字字符和中文字符出现的次数。
def count2(str1):
'''
统计字符串中数字字符和中文字符出现的次数。
:param str1: 需要统计的原字符
:return: None
'''
count_num = 0
count_cn = 0
for x in str1:
if x.isgigit():
count_num += 1
elif '/u9fa5' >= x >= '/u4e00':
count_cn += 1
print(f'中文字符:{count_cn}个,数字字符:{count_num}个')
# 练习3:定义一个函数统计一个字符串中另外一个字符串出现的次数(不能使用count方法)
def count1(str1,str2):
'''
统计一个字符串中另外一个字符串出现的次数
:param str1: 字符串1
:param str2: 字符串2(统计对象)
:return:
'''
result = str1.split(str2)
print(len(result) - 1)
# 练习4:定义一个函数,将两个长度相等的列表中的元素通过加法的方式合并成一个列表
def List_merge(list1,list2):
'''
将两个长度相等的列表中的元素通过加法的方式合并成一个列表
:param list1: 第一个列表
:param list2: 第二个列表
:return:
'''
result = [list1[index] + list2[index] for index in range(len(list1))]
3、调用函数
定义函数的时候不会执行函数体,调用函数的时候才会执行函数体(调用多少次,就执行多少次)
(1) 语法:函数名(实参列表)
(2)说明:
a. 函数名 → 需要哪个函数的功能就调用那个函数,调用那个函数就写那个函数 的 函数名;函数名必须是已经定义过的函数的函数名
b. ()→ 固定写法
c.实参列表 → 以‘数据1,数据2,数据3,…’的形式存在
实参就是从函数外部传递到函数内部的数据;
实参的个数由形参决定
(3)具体步骤:
第一步:回到函数定义位置
第二步:传参(用实参给形参赋值,传参必须保证每个形参都有值)
第三步:执行函数体
第四步:确定函数返回值(看执行函数体的时候有没有遇到return, 遇到了return,return后面的值就是返回值,没有遇到就是None)
第五步:回到函数调用的位置,继续往后执行(这个时候函数调用表达式的值就是 刚才获取的函数的返回值)
def func1(x):
# x = 100
print('hello world!', x)
print('abc')
# print(10 + 'abc')
三、函数的参数
1、位置参数和关键字参数
根据实参的提供方式不同可以将实参分为位置参数和关键字参数两种
(1)位置参数:直接将多个数据用逗号隔开,让实参和形参从位置上一一对应
(2)关键字参数:以‘形参1=数据1,形参2= 数据2,形参3=数据3,…’形式传参
注意:位置参数和关键字参数可以一起用,但是使用的时候位置参数必须在关键字参数的前 面
def func1(x, y, z):
print(f'x:{x}, y:{y}, z:{z}')
# 使用位置参数传参
func1(10, 20, 30)
func1(10, 30, 20)
# 使用关键字参数传参
func1(x=10, y=20, z=30)
func1(y=20, z=30, x=10)
# 混用
func1(100, y=200, z=300)
func1(100, 200, z=300)
# func1(x=200, y=100, 300) # 报错!
2、参数默认值
定义函数时可以以‘形参名=值’的方式给形参默认值,有默认值的形参再调用函数的时候可以不用传参
注意:如果有的形参有默认值,有的形参没有默认值,但是没有默认值的形参必须放在有默认值的形参的前面
def func2(x=1, y=2, z=3):
print(f'x:{x}, y:{y}, z:{z}')
func2()
func2(10)
func2(10, 20)
func2(10, 20, 30)
func2(x=10)
func2(y=20)
def func3(x, y, z=3):
print(f'x:{x}, y:{y}, z:{z}')
def func4(x, y=2, z=3):
print(f'x:{x}, y:{y}, z:{z}')
# def func5(x=1, y, z): # 报错!
# print(f'x:{x}, y:{y}, z:{z}')
3、参数类型说明
定义函数的时候指定参数类型:
(1)没有默认值的参数,在形参后面加‘:类型名‘
(2)给参数赋默认值
(3)返回值类型说明:类型名
def func5(str1: str, x=[]) -> int:
print(str1.split('abc'))
x.insert(100)
4、不定长参数(了解)→ 参数个数不确定
(1)对应的不定长参数:
定义函数的时候可以在某一个形参前加*,让这个参数变成一个不定长参数,可以 同时接收若干个实参
原理:让带*的形参变成一个元组,接收到的每一个实参会成为元组中元素
注意:
a.*对应的不定长参数的实参只能用位置参数传参
b.如果一个函数中既带有*的不定长参数,又带有定长参数,调用函数的时候* 前面的定长参数必须位置传参,*后面的定长参数必须用关键字参数传参
# 案例:定义一个函数,求多个数的和
# sum2(10, 20)
# sum2(1, 2, 3)
# sum2(10, 20, 30, 4, 5)
def func1(*sum1):
sum2 = 0
for x in sum1:
sum2 += x
print(sum2)
func1(4,5,6)
(2)** 对应的不定长参数:
定义函数的时候可以在某一个形参前加 ** ,让这个参数变成一个不定长参数,可 以同时接收若干个实参
原理:让带 ** 的形参变成一个元组,接收到的每一个实参会成为元组中元素
注意:调用函数的时候,** 对应的不定长参数只能使用关键字传参,而且参数名可 以是任何合法的变量名
# 这里*的作用:让*前面的定长参数必须位置传参,*后面的定长参数必须用关键字参数传参
def func1(x,y,*,z):
pass
# 让func2在调用的时候可以在任何形式调用
def func2(*args,**kwargs):
pass
四、返回值
1、什么是返回值
函数返回值就是从函数内部传递到函数外部的数值
2、为什么需要返回值
在函数内部产生的数据,如果不作为函数的返回值返回,在函数调用结束的时候这个数据会自动释放
3、什么时候需要返回值
如果实现函数的功能产生了新的数据,就需要将新的数据作为返回值返回
4、怎么将数据作为返回值返回
将需要返回的数据放在关键字return的后面:return 返回值
注意:如果执行函数体的时候没有遇到return,函数的返回值就是None
5、怎么在函数外部使用函数的返回值
函数调用表达式的值就是函数的返回值
函数调用表达式:调用函数的语句[函数名(实参列表)]
6、return的作用
(1)将数据作为函数的返回值返回
(2)提前结束函数 → 如果执行函数体的时候遇到了return,函数直接结束
五、匿名函数
匿名函数的本质是函数,普通函数中除了定义函数的语法意外其他内容都是适用于匿名函数
语法:lambda 形参列表:返回值
# 定义一个匿名函数求任意两个数的和
sum1 = lambda num1, num2 : num1 + num2
print(sum1(10, 20))
print(sum1(num1=100, num2=200))
六、实参高阶函数
1、什么是实参高阶函数
参数是函数的函数;
给参数是函数的参数传参的时候可以使用普通函数的函数名,也可以使用匿名函数
2、常见的实参高阶函数的用法(重点)
(1)max、min、sorted、列表.sort
max(序列) → 直接比较序列中元素的大小求最大值
max(序列,key=函数) → 按照函数制定的规则比较序列中元素的大小求最大值
函数的要求:a.有且只有一个参数,代表序列中的每个元素
b.需要一个返回值,返回值就是比较对象
# 案例1:求nums中个位数最大的元素
nums = [100, 98, 72, 19, 55, 37]
result = max(nums,key=lambda item:item % 10)
print(result)
print('--------------------分割线-------------------')
# 案例2:获取分数最高的学生
students = [
{'name': '张三', 'score': 98, 'age': 18, 'gender': '男'},
{'name': '李四', 'score': 82, 'age': 20, 'gender': '女'},
{'name': '王五', 'score': 77, 'age': 33, 'gender': '女'},
{'name': '张六', 'score': 99, 'age': 19, 'gender': '男'},
{'name': '何琪', 'score': 56, 'age': 22, 'gender': '女'}
]
result = max(students,key=lambda item: item['score'])
print(result)
print('--------------------分割线-------------------')
# 案例3:获取长度最长的字符串
list1 = ['mns', '你好', '123823', '就是简单']
result = max(list1,key=lambda item: len(item))
print(result)
print('--------------------分割线-------------------')
# 案例4:按照学生年龄对所有的学生从大到小排序
students = [
{'name': '张三', 'score': 98, 'age': 18, 'gender': '男'},
{'name': '李四', 'score': 82, 'age': 20, 'gender': '女'},
{'name': '王五', 'score': 77, 'age': 33, 'gender': '女'},
{'name': '张六', 'score': 99, 'age': 19, 'gender': '男'},
{'name': '何琪', 'score': 56, 'age': 22, 'gender': '女'}
]
result = sorted(students,key=lambda item:item['age'],reverse=True)
print(result)
print('--------------------分割线-------------------')
students.sort(key=lambda item:item['ag'],reverse=True)
print(students)
(2)map → 基于原序列中的元素创建一个新的序列
a.map(函数,序列) → 按照函数指定的规则基于指定序列中的元素创建一个新的序列
函数的要求:有且只有一个参数,代表序列中的每个元素 需要一个返回值,返回值就是新序列中的元素
b.map(函数,序列1,序列2)
函数的要求:有且只有两个参数,分别代表后面两个序列中的每个元素需要一个返 回值,返回值就是新序列中的元素
c.map(函数,序列1,序列2,序列3,…)
# 练习1:将nums中所有的元素都乘以10
nums = [100, 98, 72, 19, 55, 37]
result = list(map(lambda item:item * 10,nums))
print(result)
print('--------------------分割线-------------------')
# 练习2:提取students中所有学生的姓
students = [
{'name': '张三', 'score': 98, 'age': 18, 'gender': '男'},
{'name': '李四', 'score': 82, 'age': 20, 'gender': '女'},
{'name': '王五', 'score': 77, 'age': 33, 'gender': '女'},
{'name': '张六', 'score': 99, 'age': 19, 'gender': '男'},
{'name': '何琪', 'score': 56, 'age': 22, 'gender': '女'}
]
result = list(map(lambda item: item['name'][0], students))
print(result)
print('--------------------分割线-------------------')
# 练习3:基于students构建一个新的字典,字典中的元素是学生姓名和学生对应的总分的键值对。
students = [
{'name': '张三', 'math': 98, 'chinese': 80, 'english': 90},
{'name': '李四', 'math': 82, 'chinese': 55, 'english': 72},
{'name': '王五', 'math': 77, 'chinese': 80, 'english': 88},
{'name': '张六', 'math': 99, 'chinese': 95, 'english': 98},
{'name': '何琪', 'math': 56, 'chinese': 80, 'english': 62}
]
result = dict(map(lambda item:[item['name'], item['math'] + item['chinese'] + item['english']],students))
print(result)
print('--------------------分割线-------------------')
# 练习4:已知三个列表依次保存的是学生的姓名、年龄和性别,基于这些创建每个学生信息对应的新列表
names = ['张三', '李四', '王五']
ages = [19, 22, 18]
genders = ['男', '女', '男']
# [{'name': '张三', 'age': 19, 'gender': '男'}, {'name': '李四', 'age': 22, 'gender': '女'}, .....]
result =list(map(lambda x,y,z:{'name': x, 'age': y,'gender': z},names,ages,genders))
print(result)
(3)reduce → 将序列中的元素合并成一个数据
用reduce前需要写from functools import resduce
reduce(函数,序列,默认值) → 按照函数制定的规则将序列中的元素合并成一个数据
a.默认值:相加为0,相乘为1,类型转换为空
b.函数的要求:有且只有两个参数,第1个参数代表默认值,第2个参数代表序 列中的每个元素;需要一个返回值,返回值就是合并方式
七、全局变量和局部变量
可以根据变量的作用域(变量能使用的范围)不同,可以将变量分为全局变量和局部变量(定义在类中的变量是属性)
1、全局变量
没有定义在函数或者类里面的变量都是全局变量,全局变量的作用域从定义开始到程序结束
2、局部变量
定义在函数中的变量就是局部变量,局部变量的作用域是从定义开始到函数结束
3、变量保存数据的原理(存储位置)
全局变量保存在全局栈区间,全局栈区间中的数据在程序结束的时候才会自动释放;
局部变量保存在函数对应的临时栈区间,函数对应的临时栈区间调用函数的时候创建,函数调用结束的时候会自动释放
global关键字可以修改局部变量的保存位置,让局部变量储存到全局栈区间中