文章目录
十六、函数高级(续)
8. 函数返回多个值
当在函数中,需要返回多个值的情况下,可以在return 后面直接将多个值写出来
解释器在返回时,会将多个值自动进行组包
9. 函数嵌套调用及过程
- 函数嵌套
函数嵌套是指,在一个函数的函数体内,又去调用了另外一个函数
如果函数内调用了另一个函数,那么当前函数会中断执行,跳转到另一个函数位置进行执行,被调用执行结束后,才回到当前函数恢复中断,继续执行
无论函数如何调用: 谁调用函数,被调用函数执行完就返回到谁那去
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n3hyJq00-1610720718257)(Media/Xnip2019-04-19_10-08-38.png)]
一般在什么情况下使用嵌套函数?
- 封装:数据隐藏 外部无法访问“嵌套函数”。
- 贯彻 DRY(Don’t Repeat Yourself) 原则 嵌套函数,可以让我们在函数内部避免重复代码。
- 闭包:Enclosed 指的是嵌套函数(一个函数包裹另一个函数,闭包)
- LEGB规则中有提到闭包概念
- nonlocal 关键字
- nonlocal 用来声明外层的局部变量。
- global 用来声明全局变量。
def outer():
b = 10
def inner():
nonlocal b # 申明外部函数的局部变量
print('inner:', b)
b = 20
inner()
print('outer b:', b)
outer()
用于函数嵌套
10. 递归函数的调用及过程(了解)
递归调用是一种特殊的嵌套调用,即一个函数 内部 调用自己
本质就是自己调用自己
利用递归可以用简单的程序来解决一些复杂的问题。比如:
- 斐波那契数列的计算
- 汉诺塔
- 快排等问题
递归结构包括两个部分:
- 定义递归头
- 什么时候不调用自身方法。如果没有头,将陷入死循环,也就 是递归的结束条件。
- 递归体
- 什么时候需要调用自身方法。
递归步骤
- 把第 n 步的值和第 n-1 步相关联。
代码特点
- 函数内部的 代码 是相同的,只是针对 参数 不同,处理的结果不同
- 当 参数满足一个条件 时,函数不再执行
- 这个非常重要,通常被称为递归的出口,否则 会出现死循环!
- 一般用于返回值,不再调用自己。
注意,递归的缺陷
递归函数由于会创建大量的函数对象、过量的消耗内存和运算能力。在处理大量数据时,谨 慎使用。
应用:
# 阶乘
def factorial(n):
if n == 1:
return 1
return n * factorial(n-1)
print(factorial(5))
'''过程
factorial(5)
-> 5 * factorial(4) -> reutrn 5 *(4 * 3 * 2 * 1) -> 120
-> 4 * factorial(3) -> return 4 *(3 * 2 * 1)
-> 3 * factorial(2) -> reutrn 3 *(2 * 1)
-> 2 * factorial(1) -> return 2 *(1)
-> return 1
'''
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FakUllxx-1610720718259)(Media/image-20210103182805468.png)]
# 数字累加
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))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aEfes7QZ-1610720718261)(media/002_递归调用示意图.png)]
提示:递归是一个 编程技巧,初次接触递归会感觉有些吃力!在处理 不确定的循环条件时,格外的有用,例如:遍历整个文件目录的结构
1. 系统编程相关
- pass
pass
就是一个空语句,不做任何事情,一般用做占位语句- 是为了保持程序结构的完整性
- 无限循环
- 在开发软件时,如果 不希望程序执行后 立即退出
- 可以在程序中增加一个 无限循环
- 由用户来决定 退出程序的时机
- TODO 注释
- 在
#
后跟上TODO
,用于标记需要去做的工作
# TODO(作者/邮件) 显示系统菜单
**练习——函数版学生管理系统
这个程序要理解记住的是逻辑,而不是代码
(个人)需要记住
def search_stu_with_id(stu_id):
# 遍历每个学生
for stu in students:
if stu['id'] == stu_id:
return stu
# 返回没找到
else:
print(f'学号为{stu_id}的学生不存在')
return 0
这里的for-else使用:当遍历完成,即没有return stu时,执行else后的语句
退出函数exit()
exit(0) # 程序通过 exit() 方法,可以直接结束程序
- 退出码
0
:- 这里的
0
代表退出程序后显示的数字 0
一般代表正常退出
- 这里的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RBCJFYtC-1610720718267)(Media/image-20201229203007345.png)]
- 代码实现
'''
学生管理系统
程序 分析:
1. 学生怎么表示
2. 学生可能使用 学号 id ,姓名 name, 年龄 age ,可以使用一个字典来表示每一个学生
3. 应该有一个容器去保存所有的学生字典 ,可以使用列表实现
4. 应该有一个主控函数
5. 菜单函数
5-1. 选择功能的函数
6. 添加学生函数
7. 修改学生函数
8. 查找学生函数
9. 删除学生函数
10.显示所有学生的函数
11. 因为创建 学生和修改学生,都需要从键 盘输入 数据,
那么输入 数据这个功能 就可以提取出一个函数,返回输入的数据
12. 添加 一个功能 函数,用来显示每个学生的信息
'''
# 1. 定义一个学生的列表,用来保存来管理学生
students = []
# 2. 主控函数
def main():
# 通过 死循环控制程序可以重复运行
while True:
# 显示菜单
show_menu()
# 键盘输入选择一个功能
select_id = input('请输入一个功能id:')
# 根据输入调用相应的功能 函数
operator(select_id)
def show_menu():
print("---------------------------")
print(" 学生管理系统 V1.0")
print(" 1:添加学生")
print(" 2:删除学生")
print(" 3:修改学生")
print(" 4:查询学生")
print(" 5:显示所有学生")
print(" 6:退出系统")
print("---------------------------")
# 参数是传递的用来选择的功能 id
def operator(select_id):
if select_id == '1':
add_stu()
elif select_id == '2':
stu_id = input('请输入要删除的学生id:')
del_stu(stu_id)
elif select_id == '3':
modify_id = input('请输入要修改的学生id:')
modify_stu(modify_id)
elif select_id == '4':
stu_id = input('请输入要查找的学生id:')
stu = search_stu_with_id(stu_id)
show_stu_info(stu)
elif select_id == '5':
show_all_info()
elif select_id == '6':
exit(0)
else:
print('输入错误,请重新输入')
# break # 因为选择功能没有在循环中,所以不能break
# return # 作用同上,函数返回后,返回到调用处,返回到主控函数中的死循环中,所以程序还是不能结束
# 程序通过 exit() 方法,可以直接结束程序
# 实现一个输入函数
# 用来从键盘获取学生信息,并返回
# 学号 id ,姓名 name, 年龄 age
def input_stu_info():
# 保存输入的学生信息
print('请输入学生信息:')
id = input('请输入学号 id:')
name = input('请输入姓名 name:')
age = input('请输入年龄 age:')
# 同时返回多个数据时,会组包成一个元组
stu = id, name, age
return stu
# 实现 添加函数
def add_stu():
# 主体思路 就是向列表 中添加 一个字典
# 创建一个学生字典 ,空的
stu_dict = {}
# 调用 输入函数,获取学生信息
stu_tup = input_stu_info()
# 利用获取的信息为字典添加数据
stu_dict['id'] = stu_tup[0]
stu_dict['name'] = stu_tup[1]
stu_dict['age'] = stu_tup[2]
# 将字典加到列表中
global students
students.append(stu_dict)
# print(stu_list)
# 实现学生查找的功能
# 返回被找到的学生
def search_stu_with_id(stu_id):
# 遍历每个学生
for stu in students:
if stu['id'] == stu_id:
return stu
# 返回没找到
else:
print(f'学号为{stu_id}的学生不存在')
return 0
# 实现一个用来显示 单个学生信息的函数
def show_stu_info(stu):
if stu != 0:
print(f'学号:{stu["id"]},姓名:{stu["name"]},年龄:{stu["age"]}')
# 实现删除学生的函数
def del_stu(del_id):
# 找到要删除的学生
stu_dict = search_stu_with_id(del_id)
# 从列表中删除
if stu_dict == 0:
print('删除失败')
else:
students.remove(stu_dict)
print('删除成功')
# 实现 修改学生的函数
def modify_stu(modify_id):
# 查找学生
stu = search_stu_with_id(modify_id)
# 如果找到就修改
if stu == 0:
print('修改失败')
else:
del_stu(modify_id)
add_stu()
print('修改成功')
# 先去调用 输入 函数获取数据
# 利用获取的信息为字典添加数据
# 显示所有学生信息
def show_all_info():
# 遍历打印
for stu in students:
show_stu_info(stu)
# 执行主控函数,运行程序
main()
12. 函数的引用,函数名的赋值及函数作参数传递
- 将一个函数的引用赋值给变量
def show():
print('hello python')
show()
#将一个函数的引用赋值给变量
func = show # 注意这里不能加'()',因为加了括号代表调用执行这个函数
print(func) # 输出:<function show at 0x000002880AA041F8>
func() # 调用了show()
- 注意这里不能加
()
,因为加了括号代表调用执行这个函数 - 同样可以通过新的
变量名()
,来调用这个函数 - 作用:可以实现一个可以调用另外一个函数的函数
# 可以实现一个可以调用另外一个函数的函数
def show():
print('hello python')
def call_function(func):
func()
call_function(show) # show不带括号
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WjIOwpCa-1610720718270)(Media/Xnip2019-04-20_09-58-21.png)]
- 函数作为参数传递
函数作为参数传递时,同样不需要加括号,只需传递函数名即可
因为如果加了括号,定于是运行了这个函数
12. 匿名函数 lambda
def 函数名(参数列表):
函数体
- 格式:
变量 = lambda [形参1], [形参2], ... : 函数体只能是 [单行表达式] 或 [函数调用]
func = lambda: 1 + 1
print(func) # <function <lambda> at 0x0000022D46A540D8>
print(func()) # 2
func = lambda x: print(x ** 10)
func(2) # 1024
- lambda定义和普通函数的区别:
- lambda 没有函数名
- lambda 参数列表外没有括号
- lambda 函数体中,只能实现简单的表达式计算或函数调用
- lambda 函数体中,不能使用Return,if,while,for-in 这些都不行
- return:lambda函数默认返回表达式的计算结果,不需要return
- lambda 函数体中,可以使用if 实现的三目运算符.
func = lambda m, n: m if m > n else n
print(func(1, 2)) # 2
- 使用场景:
一般情况下,因为lambda的局限性,使得他不能实现复杂功能,只能实现一些简单功能
那么在使用时,一般会实现一个简单的,一次性使用的场景
eval
函数
功能:
eval()
- 将字符串 当成 有效的表达式 来求值 并返回计算结果
- 或将字符串当成有效代码来执行
语法:
eval(source[, globals[, locals]]) -> value
参数:
- source:一个 Python 表达式或函数 compile()返回的代码对象
- globals:可选。必须是 dictionary
- locals:可选。任意映射对象
# 基本的数学计算
In [1]: eval("1 + 1")
Out[1]: 2
# 字符串重复
In [2]: eval("'*' * 10")
Out[2]: '**********'
# 将字符串转换成列表
In [3]: type(eval("[1, 2, 3, 4, 5]"))
Out[3]: list
# 将字符串转换成字典
In [4]: type(eval("{'name': 'xiaoming', 'age': 18}"))
Out[4]: dict
s = "print('abcde')"
eval(s) # abcde
a = 10
b = 20
c = eval("a+b")
print(c) # 30
dict1 = dict(a=100,b=200)
d = eval("a+b",dict1)
print(d) # 300
案例 - 计算器
需求
-
提示用户输入一个 加减乘除混合运算
-
返回计算结果
input_str = input(“请输入一个算术题:”)
print(eval(input_str))
不要滥用 eval
eval 函数会将字符串当做语句来执行,因此会被注入安全隐患。比如:字符串中含有删除文 件的语句。那就麻烦大了。因此,使用时候,要慎重!!!
在开发时千万不要使用
eval
直接转换input
的结果
__import__('os').system('ls')
-
等价代码
import os
os.system(“终端命令”)
-
执行成功,返回 0
-
执行失败,返回错误信息
十七、高阶函数
1. map()函数
- map()关系映射函数
map()
:会根据提供的函数对指定序列做映射
my_list = [i for i in range(5)]
print(my_list) # [0, 1, 2, 3, 4]
# 这个函数是为了给map的参数进行传参
# 所以这个函数有且只能有一个参数
def f(x):
return x ** 2
result = map(f, cl)
print(type(result)) # <class 'map'>
print(result) # <map object at 0x000001FB8931F088>
print(list(result)) # [0, 1, 4, 9, 16]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GFf3pfOX-1610720718272)(Media/IMG_1138(20201230-112433)].JPG)
- 优化——利用lambda进行传参
# 利用lambda替代函数进行传参
my_list = [1, 2, 3, 4, 5]
result = map(lambda x: x ** 2, my_list)
print(type(result)) # <class 'map'>
print(result) # <map object at 0x000001DE3C291048>
print(list(result)) # [1, 4, 9, 16, 25]
- 自定义实现map函数
# 自定义实现map函数
def my_map(func, c_list):
result = []
for i in c_list:
result.append(func(i))
return result
my_list = [1, 2, 3, 4, 5]
result = my_map(lambda x: x ** 2, my_list)
print(type(result)) # <class 'map'>
print(result) # <map object at 0x000001DE3C291048>
print(list(result)) # [1, 4, 9, 16, 25]
2. reduce()函数
- 使用
reduce()
:函数会对参数序列中的元素进行累计
函数将一个数据集合中的所有数据进行下列操作
- 用传给reduce中的函数function(有两个参数)先对集合中的第1,2个元素进行操作
- 得到的结果在于第三个数据用function函数运算,最后得到一个结果
import functools
my_list = list('hello') # ['h', 'e', 'l', 'l', 'o']
result = functools.reduce(lambda s1, s2: s1 + s2, my_list)
print(result) # 'hello'
- 应用——用reduce()求阶乘
# 练习——使用reduce求阶乘
import functools
my_list = [i for i in range(1, 6)] # [1, 2, 3, 4, 5]
result = functools.reduce(lambda s, n: s * n, my_list)
print(result) # 120
3. filter()函数
filter()
:用于过滤序列,过滤掉不符合调价按的元素,返回一个filter对象
- 如果要转换为列表,可以使用list()来转换
# 过滤偶数
my_list = [i for i in range(1, 11)] # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
new_list = list(filter(lambda x: x % 2 == 0, my_list))
print(new_list) # [2, 4, 6, 8, 10]
# 过滤出所有的数字字符串
my_list = ['123', '234', 'abc', '@#$', ' ', 'abc234', '132abc']
num_list = list(filter(lambda s: s if s.isdigit() else None, my_list))
print(num_list) # ['123', '234']
4. sort()函数高级
通过lambda指定key,根据key值对列表中的字典进行排序
my_list.sort(key=lambda d: d[‘id’])
my_list = [
{'id': 3, 'name': 'Tom', 'age': 18},
{'id': 1, 'name': 'Jack', 'age': 21},
{'id': 2, 'name': 'Rose', 'age': 20}
]
print(my_list)
# [{'id': 3, 'name': 'Tom', 'age': 18},
# {'id': 1, 'name': 'Jack', 'age': 21},
# {'id': 2, 'name': 'Rose', 'age': 20}]
my_list.sort(key=lambda d: d['id'])
print(my_list)
# [{'id': 1, 'name': 'Jack', 'age': 21},
# {'id': 2, 'name': 'Rose', 'age': 20},
# {'id': 3, 'name': 'Tom', 'age': 18}]
# 年龄降序排序
my_list.sort(key=lambda d: d['age'], reverse=True)
print(my_list)
# [{'id': 1, 'name': 'Jack', 'age': 21},
# {'id': 2, 'name': 'Rose', 'age': 20},
# {'id': 3, 'name': 'Tom', 'age': 18}]