高阶函数
变量可以指向函数,函数的参数也能接受变量。当一个函数可以接收另一个函数作为参数时,这个函数被称作高阶函数。
最简单的高阶函数如下:
1 | def add(x,y,f): |
map/reduce函数
map()函数
map()函数接收两个参数,一个是函数,另一个是Iterable,map将传入的函数依次作用到序列的每个元素,并将结果作为新的Iterable返回。
例如要将函数f(x)=x^2作用到一个list[1,2,3,4,5,6,7,8,9]上:
1 | def f(x): |
reduce()函数
reduce()把函数作用到一个序列[x1,x2,x3,…]上,reduce接收两个函数后,把结果继续和序列的下一个元素做累计计算。
例如要对一个序列求和:
1 | from functools import reduce |
将map()与reduce()函数结合,可以写出把str转换为int的函数:
1 | from functools import reduce |
练习题
利用map()函数,把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:['adam', 'LISA', 'barT'],输出:['Adam', 'Lisa', 'Bart']:
代码如下:
1 | def normalize(name): |
Python提供的sum()函数可以接受一个list并求和,请编写一个prod()函数,可以接受一个list并利用reduce()求积:
1 | from functools import reduce |
利用map和reduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456:
1 | #方法一: |
filter函数
filter()函数用于过滤序列,该函数接收一个函数和一个序列。与map()不同的是,filter()把传入的函数依次作用于每个元素后,根据返回值是True还是False来决定是保留还是丢弃该元素。
例如在一个list中删掉偶数,只保留奇数:
1 | def odd(n): |
filter()函数返回
的是一个Iterator,因此要强迫filter()完成计算结果就需要用list()函数来获得所以结果并返回list。
用filter求1000以内素数例子:
1 | def _odd_iter(): #先构造从3开始的奇数列 |
练习题
回数是指从左向右读和从右向左读都是一样的数,例如12321,909。请利用filter()筛选出回数:
代码如下:
1 | def is_palindrome(n): |
sorted函数
sorted()可以对list进行排序,还可以接收一个key函数来实现自定义排序。
比如按绝对值大小排序:
1 | print(sorted([36,5,-12,9,-21],key=abs)) |
默认情况下,用sorted()对字符串排序是按照ASCII的大小比较的,若要忽略大小写按照字母表排序,则可以写成:
1 | #正序排序 |
练习题
假设我们用一组tuple表示学生名字和成绩:
1 | L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)] |
请用sorted()对上述列表分别按名字排序:
代码如下:
1 | L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)] |
再按成绩从高到低排序:
1 | L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)] |
返回函数
高阶函数除了可以接收函数作为参数外,还可以把函数作为结果值返回。
例如,实现一个可变参数的求和,根据需要计算结果:
1 | def lazy_sum(*args): |
在调用lazy_sum()函数时,返回的是求和函数而不是求和结果。调用函数f时才真正计算求和的结果。当调用lazy_sum()函数时,每次调用都会返回一个新的函数,即使传入相同参数,返回的函数也不会相同。
闭包
上面的例子中,内部函数sum可以引用外部函数lazy_sum()的参数和局部变量。当lazy_sum()返回函数sum时,相关参数和变量仍保存在返回的函数中。这种程序结构便称为闭包。
例如,在下面例子中f1(),f2()和f3()调用的结果应该是1,4,9,但实际结果是9 9 9。这便是由于返回函数引用了变量i,但它并非立刻执行,等到3个函数都返回时,所引用的变量已经变成了3,故最终结果为9。
1 | def count(): |
注意:返回函数不要引用任何循坏变量或后续会发生变化的变量。若一定要引用循环变量,便再创建一个函数,用该函数的参数绑定循环变量当前的值,具体操作如下:
1 | def count(): |
nonlocal
使用闭包,就是内层函数引用了外层函数的局部变量。如果只读外层变量的值,返回的闭包函数调用一切正常,但对外层函数的局部变量进行运算就会报错。
若内层函数想要计算外层函数的局部变量,需要在内部函数内部加一个nonlocal 局部变量名的声明。因此在使用闭包时,对外层变量赋值前,需要使用nonlocal声明改变了不是当前函数的局部变量。
练习题
利用闭包返回一个计数器函数,每次调用它返回递增整数:
代码如下:
1 | def createCounter(): |
匿名函数
传入函数时,有时不需要显示定义函数,直接传入匿名函数更方便。如匿名函数lambda x:x*x实际上就是:
1 | def f(x): |
匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。由于匿名函数没有名字,不必担心函数名冲突。匿名函数是一个函数对象,可以把匿名函数赋值给一个变量,再利用变量来调用该函数,也可以把匿名函数作为返回值返回。
练习题
请用匿名函数改造下面的代码:
1 | def is_odd(n): |
代码如下:
1 | print(list(filter(lambda n:n % 2 == 1, range(1, 20)))) |