今天和大家聊一聊Python 中的高阶函数:map、reduce、filter、sorted
首先了解一下什么是高阶函数,
高阶函数 就是让函数的参数能够接受别的函数,比如:
1 def add(x, y, f): 2 return f(x) + f(y) 3 4 result = add(-5, 6, abs) 5 print(result) # 11
再比如:
1 list1 = [1, 2, 3, 4, 5, 6] 2 list2 = [32, 11, 55, 46, 89, 43] 3 def add_total(l1, l2, fun): 4 return fun(l1) + fun(l2) 5 6 result = add_total(list1, list2, min) 7 print(result) # 12 8 result = add_total(list1, list2, max) 9 print(result) # 95
那么Python 中这些高阶函数具体的作用是什么呢?不要着急,往下面看看
一、map 函数,接受两个参数,一个是函数,另一个是迭代对象Iterable ,将迭代对象Iterable 里面的每个元素作用到这个函数里面后,生成新的map 对象(是迭代器对象Iterator),并返回,比如:
1 def f_double(x): 2 return 2 * x 3 4 list1 = [1, 2, 3, 4, 5, 6] 5 m = map(f_double, list1) 6 # print(list(m)) # [2, 4, 6, 8, 10, 12] 7 print(m) # <map object at 0x00000259F8CCE102> 注意这个map 函数返回的是一个map 对象,这个map 对象是一个迭代器对象,所以可以调用next() 函数,也可以去使用for 循环进行循环遍历 8 print(next(m)) # 2 注意,需要注释上面的print(list(m))并重新运行,否则会报StopIteration 的错误
二、reduce 函数, 和map 函数一样接受两个参数,第一个是函数,第二个是序列Iterable ,不同是reduce 把结果继续和序列的下一个元素做累积运算,效果如下:
1 # reduce(fn, [x1, x2, x3, x4]) = fn(fn(fn(x1, x2), x3), x4) 2 3 def multiple(x, y): 4 return x * 10 + y 5 6 from functools import reduce 7 list1 = [1, 3, 5, 7, 9] 8 r = reduce(multiple, list1) 9 print(r) # 13579
这个例子本身没多大用处,但是考虑到字符串str 也是一个序列,对上面的例子稍加改动,配合map()
就可以写出str 转换为int 的函数:
1 from functools import reduce 2 3 DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, 4 '5': 5, '6': 6, '7': 7, '8': 8, '9': 9} 5 def multiple(x, y): 6 return x * 10 + y 7 8 def char2num(s): 9 return DIGITS[s] 10 11 r = reduce(multiple, map(char2num, '2468')) 12 print(r) # 2468
下面是廖老师出的几个问题,同学们可以思考一下,并参考一下我这边给出的代码,看看还有没有什么可以优化,或者我没考虑的地方,欢迎留言指导~
Q1.综合写一个不借助int() 函数,将字符串转换成数字的方法:
1 from functools import reduce 2 3 DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, 4 '5': 5, '6': 6, '7': 7, '8': 8, '9': 9} 5 def str2num(s): 6 m = map(lambda x: DIGITS[x], s) 7 try: 8 r = reduce(lambda x, y: x*10+y, m) 9 except KeyError: 10 print('请输入纯数字的字符串!') 11 else: 12 return r 13 14 result = str2num('3234cc4') 15 print(type(result)) 16 print(result) 17 18 # 请输入纯数字的字符串! 19 # <class 'NoneType'> 20 # None 21 22 result2 = str2num('32344') 23 print(type(result2)) 24 print(result2) 25 26 # <class 'int'> 27 # 32344
Q2.利用map() 函数,把用户输入的不规范的英文名字,变为首字母大写,其他小写
1 def normalize(name): 2 try: 3 if not name.isalpha(): 4 return 5 except AttributeError: 6 print("您输入的:{} 不是正确的姓名".format(name)) 7 else: 8 return name.capitalize() 9 # result = str(name).capitalize() 10 # return result 11 12 L1 = ['adam', 'LISA', 'barT1', 123, 0, True, '123333', [23,11], (11, 33), "丽丽"] 13 L2 = [x for x in map(normalize, L1) if x] 14 print('你输入的最终姓名名单为:{}'.format(L2)) 15 16 17 # 您输入的:123 不是正确的姓名 18 # 您输入的:0 不是正确的姓名 19 # 您输入的:True 不是正确的姓名 20 # 您输入的:[23, 11] 不是正确的姓名 21 # 您输入的:(11, 33) 不是正确的姓名 22 # 你输入的最终姓名名单为:['Adam', 'Lisa', '丽丽']
Q3.利用map 和reduce 编写一个str2float 的函数,比如把字符串‘123.456’ 转换成浮点数123.456
1 def str2float(s): 2 i1, f1 = split_int_float(s) 3 int_res = char2num(i1) 4 float_res = char2num(f1) / 10**len(f1) 5 return int_res + float_res 6 7 def split_int_float(s): 8 # 以小数点为分隔区,返回整数部分和小数部分 9 str_int, str_float = s.split('.')[0], s.split('.')[1] 10 return str_int, str_float 11 12 def char2num(char): 13 DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9} 14 from functools import reduce 15 num = reduce(lambda x, y:x*10+y, map(lambda x:DIGITS[x], char)) 16 return num 17 18 19 print('str2float(\'123.456\') =', str2float('123.456')) 20 if abs(str2float('123.456') - 123.456) < 0.00001: 21 print('测试成功!') 22 else: 23 print('测试失败!') 24 25 26 # str2float('123.456') = 123.456 27 # 测试成功!
三、filter 函数 也是接收一个函数和一个序列,filter()把传入的函数作用于序列的每个元素,然后根据返回值是True 还是False 决定保留还是丢弃该元素
1.比如把一个序列中的非字符串删掉,可以这么写:
1 def not_empty(s): 2 try: 3 s.strip() 4 except AttributeError: 5 print('请输入字符串') 6 else: 7 return s and s.strip() 8 9 list_filter = list(filter(not_empty, ['A', '', 'B', None, 'C', ' ', 'B C', ' VV', 123])) 10 print(list_filter) 11 12 # 请输入字符串 13 # 请输入字符串 14 # ['A', 'B', 'C', 'B C', ' VV']
2.回数,值从左向右和从右向左读都是一样的数,如:12321,909,787,66266
1 def is_palindrome(n): 2 return str(n) == str(n)[::-1] 3 4 # backnum = filter(is_palindrome, range(1, 100)) 5 backnum = filter(lambda x: str(x) == str(x)[::-1], range(1, 100)) 6 7 print('1~100的回数为:', list(backnum)) # 1~100的回数为: [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99]
四、sorted 函数,可以对list 进行排序:list 的元素,可以是数值,直接比较大小,也可以是字母,会按照ASCII 码比较
注意,sorted 函数和 列表的sort() 方法是有区别的
sorted(list1), 对list1 进行排序,并作为一个新的列表返回,list1 不变
ist1.sort(), 对list1 本身进行排序,改变的将是list1
前面说了sorted 函数也是一个高阶函数,它可以接收一个参数key 来实现自定义排序,key 的值是别的函数,或者是匿名函数。例如按照绝对值大小排序:
1 list4 = [-1, 9, -6, 55, 33, -5] 2 sort_list = sorted(list4) 3 abs_list = sorted(list4, key=abs) 4 print(sort_list) # [-6, -5, -1, 9, 33, 55] 5 print(abs_list) # [-1, -5, -6, 9, 33, 55] 6 7 list5 = ['bob', 'about', 'Zoo', 'Credit'] 8 res = sorted(list5) 9 print(res) # ['Credit', 'Zoo', 'about', 'bob']
默认情况下,对字符串排序,是按照ASCII 的大小比较的,由于 在ASCII 中 'Z' < 'a', 所以‘Z’会排在‘a’前面
忽略大小写的做法:
1 list5 = ['bob', 'about', 'Zoo', 'Credit'] 2 res_sort = sorted(list5, key=str.lower) 3 print(res_sort) # ['about', 'bob', 'Credit', 'Zoo']
反序排序列,不必动用key 函数,可以传入第三个参数reverse=True,如:
1 list5 = ['bob', 'about', 'Zoo', 'Credit'] 2 res_reverse_sort = sorted(list5, reverse=True) 3 ignore_reverse_sort = sorted(list5, key=str.lower, reverse=True) 4 print(res_reverse_sort) # ['bob', 'about', 'Zoo', 'Credit'] 5 print(ignore_reverse_sort) # ['Zoo', 'Credit', 'bob', 'about']
sorted 函数的最最常用的场景,也是面试中经常会遇到的问题,给一个元素是元祖的列表按照元素第二个元素排序,或者是对一个字典的Value 进行排序,如下:
1 D1 = {'Bob': 75, 'Adam': 92, 'Bart': 66, 'Lisa': 88} 2 L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)] 3 D2 = {'name': 'Lucy', 'age': 18, 'id': '110', 'sex': 'women'} 4 print(sorted(L, key=lambda x: x[0].lower())) # 按照人名排序,(不分大小写) 5 print(sorted(D1.items(), key=lambda x: x[1])) # 相当于先把D1 转换成L ,然后进行第7 行代码 6 print(sorted(D2.items(), key=lambda x: x[0].lower())) # 按照字典的K 排序,不分大小写 7 print(sorted(L, key=lambda x: x[1])) # 将L 按照成绩从小到大排序 8 print(sorted(L, key=lambda x: -x[1])) # 将L 按照成绩从大到小排列 9 10 11 12 [('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)] 13 [('Bart', 66), ('Bob', 75), ('Lisa', 88), ('Adam', 92)] 14 [('age', 18), ('id', '110'), ('name', 'Lucy'), ('sex', 'women')] 15 [('Bart', 66), ('Bob', 75), ('Lisa', 88), ('Adam', 92)] 16 [('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]
至此,这四个Python 比较常用的高阶函数介绍完毕了,希望能给大家的学习带来一定的帮助
参考:高阶函数-廖雪峰的官方网站:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014317849054170d563b13f0fa4ce6ba1cd86e18103f28000