day11 函数的进阶
一、return
return只能在函数体中使用
1. 返回值
将数据作为函数的返回值使用
return 数据
2. 结束函数
执行函数体的时候如果遇到return,函数直接结束。
def func1():
print("+++++")
print('------')
func1()
def func2():
print('++++++')
return 100 # 若return后不加数据表示到此结束函数体,返回值为None
print('-----') # return后的不会使用
func2() # ++++++ 执行函数体的结果
print(func2()) # ++++++
# 100 执行函数体并返回值
def func3(n):
for x in range(1, n):
if x % 3 == 0:
return x
print(f'x:{x}')
print(func3(5))
# x:1
# x:2
# 3
二、变量作用域
变量作用域:变量定义完之后使用的有效范围
根据变量作用域的不同,可以将变量分为全局变量和局部变量两种。
1. 全局变量
Pythonz中没有定义在函数中或者类中的变量默认都是全局变量(包括循环中定义的变量)
作用域:从定义开始到程序结束
a = 100 # a 是全局变量
for x in range(2):
b = 200
print(b)
print(x)
# x 和 b 都是全局变量
2. 局部变量
定义在函数中的变量
作用域:从定义开始到函数结束
# m 和 n 是局部变量
def func2(m):
n = 1000
print(f'函数里面m:{m}')
print(f'函数里面n:{n}')
func2(100)
# 函数里面m:100
# 函数里面n:1000
# print(f'函数外m:{m}')
# print(f'函数外n:{n}') 报错
补充:定义在类中的变量不是局部变量,而是属性,是不同于局部变量和全局变量的一个概念。
3. 全局变量和局部变量的存储原理
1)全局变量默认保存在全局栈区间,全局栈区间会在程序结束后自动释放
2)调用函数时系统会自动为这个函数创建一个临时栈区间,用来保持在函数中产生的数据
局部变量就是保存在函数对应的临时栈区间的,函数对应的临时栈区间会在函数调用结束时自动释放
3)在函数中可以通过关键字global修改局部变量的保存方式
-
用global在函数内部定义全局变量
def func1(m): # m仍是局部变量 global n # 此时n是全局变量, 在函数外部也可以使用 n = 100 print(n) func1(100) print(n)
-
global在函数内部修改全局变量的值
name = '小明'
def fun2():
global name
name = '小花'
fun2()
print(name) # 小花
三、匿名函数
函数名=lambda 形参列表:返回值
功能相当于:
def 函数名(形参列表):
return 返回值
注意:
-
匿名函数形参至少一个(def 可以没有形参)
-
匿名函数的调用和普通函数一样
-
参数类型说明在匿名函数中不能使用
# 案例:定义一个匿名函数,求任意两个数的和 sum_2 = lambda num1, num2: num1+num2 # 调用: result = sum_2(100, 200) print(result) # 300 # 练习:定义一个匿名函数,判断指定的年是否是闰年 run_year = lambda year:(year % 4 == 0 and year % 100 !=0) or year % 400 == 0 result = run_year(2020) print(result)
四、实参高阶
函数的参数是函数的函数就是实参高阶函数
给参数是函数的参数传参:a. 使用普通函数函数名 b. 使用匿名函数
重点:掌握系统或第三方库提供的实参高阶函数的用法
常见的实参高阶:max、min、sorted、列表.sort、map、reduce……
注意:在定义 函数中调用的函数时,形参个数要相对应。也要有返回值
# 此时func1就是一个实参高阶函数
def func1(x):
x()
# func1() # 此时这里传参应该传函数,普通函数和匿名函数都可以
# 观察发现传参的参数必须是没有参数的,因为func1中调用函数就是无形参的
def func3(x):
print(x(10, 20) + 20)
# x(10,20)有两个形参,故传参的函数须得有两个形参
# x(10,20)为返回值,故参数需要有返回值
func3(lambda n1, n2: n1+n2) # 50
五、常见的实参高阶函数
1. max
-
max(序列,key=函数名) — 普通函数
-
max(序列,key=函数) — 匿名函数
max(序列,key=函数) — 按照函数制定的规则比较序列中元素的大小获取最大值
函数要求:
a. 有且只有一个参数(这个参数代表的是序列中的每个元素)
b. 有一个返回值(返回值就是返回对象)若函数为普通函数,则key=函数名 即可,不用加括号。
nums = [83, 67, 19, 22, 80, 77, 93] print(max(nums))
案例1:求个位数最大的元素
result = max(nums,key=lambda i: i%10)
print(result)
# 案例:求绝对值最大的元素
nums = [8, -283, 89, 100, 82, -34]
result1 = max(nums, key=lambda i: i ** 2)
print(result1)
# 案例:求分数最高的的学生
students = [
{'name': 'stu1', 'age': 16, 'score': 90},
{'name': 'stu2', 'age': 15, 'score': 96},
{'name': 'stu3', 'age': 26, 'score': 60},
{'name': 'stu4', 'age': 25, 'score': 45},
{'name': 'stu5', 'age': 63, 'score': 83}
]
result2 = max(students, key=lambda i: i['score'])
print(result2)
2. min
# 练习2:求nums中十位数最小的元素
nums = [92, 129, 37, 99, 150, 501]
result2 = min(nums, key=lambda i: i//10)
print(result2) # 37
3. sorted
# 练习3:将所有的学生按照年龄值从小到大排序
students = [
{'name': 'stu1', 'age': 18, 'score': 90},
{'name': 'stu2', 'age': 22, 'score': 98},
{'name': 'stu3', 'age': 25, 'score': 78},
{'name': 'stu4', 'age': 19, 'score': 81},
{'name': 'stu5', 'age': 20, 'score': 92}
]
result3 = sorted(students, key=lambda i: i['age'])
print(result3)
练习4:求nums各个位数之和最大的元素
# 方法1:
nums = [123, 78, 90, 201, 192, 330]
result4 = max(nums, key=lambda i: sum(int(x) for x in str(i)))
print(result4)
# 练习4 方法2:
nums = [123, 78, 90, 201, 192, 330]
result5 = max(nums, key=lambda i: eval('+'.join(str(i))))
print(result5)
# 练习4 方法3: 用普通函数,参数仍然看作序列中的每一个元素
def sum_1(i):
sum1 = 0
for x in str(i):
sum1 += int(x)
return sum1
result = max(nums, key=sum_1)
print(result)
4. map
map — 基于一个或多个序列的元素产生一个新序列
map(函数,序列)
-
map(函数, 序列) — 通过函数描述的规则基于序列中的元素创建一个新的序列
函数的要求:
a. 有且只有一个参数(参数代表后面这个序列的每个元素)
b. 有一个返回值(返回值就是新序列中元素) -
map(函数, 序列1, 序列2) — 只用于最后结果产生过程是两个序列元素一一对应的才行
函数要求:
a. 有且只有两个参数(两个参数分别代表后面两个序列中的每个元素)
b. 有一个返回值(返回值就是新序列中元素) -
map(函数, 序列1, 序列2, 序列3……)
# 练习1:获取names中每个人的姓
names = ['何晓东', '张三', '李四', '王五', '王二', '赵六']
result = map(lambda i: i[0], names)
print(list(result)) # ['何', '张', '李', '王', '王', '赵']
# 练习2:
students = [
{'name': 'stu1', 'age': 18, 'score': 90},
{'name': 'stu2', 'age': 22, 'score': 98},
{'name': 'stu3', 'age': 25, 'score': 78},
{'name': 'stu4', 'age': 19, 'score': 81},
{'name': 'stu5', 'age': 20, 'score': 92}
]
subjects = ['电子信息', '金融数学', '园林设计', '经济', '计算机软件']
# ['电子信息-stu1', '金融数学-stu2',....]
result1 = map(lambda i1, i2: '-'.join([i1, i2['name']]), subjects, students)
print(list(result1))
# '电子信息-stu1', '金融数学-stu2', '园林设计-stu3', '经济-stu4', '计算机软件-stu5']
# 方法:也可以不用join ,用 字符串拼接 或者 f'{i1}-{i2["name"]}'
5. reduce
reduce (函数,序列,初始值) — 按照函数规定的规则将序列中所有元素合并为一个数据。例如:
nums = [12, 301, 40, 55, 112]
把序列中所有数据 加起来、乘起来、个位数的和、合并为一个数据:123014055112 ……变成一个数据
函数的要求:
a. 有且只有两个参数(第一个参数指向初始值,第二个参数代表序列中每个元素)
b. 需要一个返回值(返回值就是合并规则)
初始值:具体看合并方式。
累计求数值和,初始值为0
累计乘积,初始值是1
字符串合并,初始值是空串
用reduce需要先导入: from functools import reduce
from functools import reduce
nums = [12, 301, 40, 55, 112]
# 案例1:把序列中所有数据 加起来
result = reduce(lambda x, i: x+i, nums, 0)
print(result)
# 案例2:把序列中所有数据 乘起来
result = reduce(lambda x, i: x*i, nums, 1)
print(result)
# 案例3:把序列中所有数据合并为一个数据:123014055112
result = reduce(lambda x, i: x+str(i), nums, '')
print(result)
# 案例4:求序列中所有数据的个位数的和
result = reduce(lambda x, i: x + i % 10, nums, 0)
print(result)
六、迭代器
1. 什么是迭代器(iter)
-
迭代器是容器型数据类型(可以遍历,也可以转换成列表), 无法直接提供一个迭代器,只能将其他序列转换成迭代器
迭代器数据只出不进,用一个少一个。 -
特点:
a. 打印迭代器时,无法查看元素有哪些
b. 无法通过len获取迭代器中元素的个数
c. 如果要使用迭代器中的元素,必须将元素从迭代器中取出来(取走),取走的元素会从迭代器中永远消失(用一个少一个) -
任何数据都可以作为迭代器的元素
# 1)创建迭代器 i1 = iter('abc') i2 = iter([10, 20, 30]) # 若将字典转换为迭代器,是去其键 # 2)打印迭代器 print(i1) # <str_iterator object at 0x0000016A63920190> print(i2) # <list_iterator object at 0x0000016A639201F0> # 3) 迭代器无法统计个数 # print(len(i1)) --- 会报错
2. 获取迭代器中的元素
只支持查(增删改没有意义)
无论以任何方式获取到了迭代器的某个元素,那么这个元素就一定会从迭代器中消失
且获取的只能是当时迭代器最上面的那个元素
1)获取单个:
next(迭代器) – 获取迭代器最前面的元素
2)遍历迭代器
for 变量 in 迭代器: — 依次获取(取走)迭代器中的元素
循环体
print(next(i1)) # a
print(next(i1)) # b
print(next(i1)) # c
# print(next(i1)) # 此时迭代器是空的,所以会报错:StopIteration
print(next(i2)) # 10
print(list(i2)) # [20, 30]
# print(next(i2)) # 会报错,因为list(i2)以转换的方式已经取完了数据,寻找迭代器是空的