高阶函数Higher-order function:一个函数的参数是另外一个函数(复合函数?)
特点1:变量可以指向函数(f→abs,f(-10)=10)
特点2:函数名(abs()函数中的abs)也是变量(如果指定abs=10,就无法启用abs()函数了。要恢复,重启Python交互环境。)
map(function,iterable):根据提供的函数对指定序列做映射。map
将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator
返回。
>>> def f(x):
... return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
map()
传入的第一个参数是f
,即函数对象本身。由于结果r
是一个Iterator
,Iterator
是惰性序列,因此通过list()
函数让它把整个序列都计算出来并返回一个list。
其实,不需要map()
,通过循环也能实现计算结果:
L = []
for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]:
L.append(f(n))
print(L)
事实上,map()
作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x2,还可以计算任意复杂的函数,比如,把这个list所有数字转为字符串:
>>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
['1', '2', '3', '4', '5', '6', '7', '8', '9']
reduce(function,iterable [,initializer]):将一个数据集合(list,tuple等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
问题:如何对一个序列各元素进行求和。
>>> from functools import reduce
>>> def add(x, y):
... return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25
当然求和运算可以直接用Python内建函数sum(),没必要动用reduce。
sum(iterable [,start]):对数据集合(list,tuple等)进行求和计算。(iterable -- 可迭代对象,如:列表、元组、集合。start -- 指定相加的参数,如果没有设置这个值,默认为0。)
但是,如果要把列表[1, 3, 5, 7, 9] 变换成整数13579,就可以通过reduce实现。
>>> from functools import reduce
>>> def fn(x, y):
... return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579
因为字符串str
也是一个序列,对上面的例子稍加改动,配合map()
,我们就可以写出把str
转换为int
的函数:
>>> from functools import reduce
>>> def fn(x, y):
... return x * 10 + y
...
>>> def char2num(s):
... digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
... return digits[s]
...
>>> reduce(fn, map(char2num, '13579'))
13579
整理成一个str2int
的函数就是
from functools import reduce
DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
def str2int(s):
def fn(x, y):
return x * 10 + y
def char2num(s):
return DIGITS[s]
return reduce(fn, map(char2num, s))
还可以用lambda函数进一步简化
from functools import reduce
DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
def char2num(s):
return DIGITS[s]
def str2int(s):
return reduce(lambda x, y: x * 10 + y, map(char2num, s))
filter(function, iterable):用于过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,如果要转换为列表,可以使用 list() 来转换【因为filter()
函数返回的是一个Iterator
,也就是一个惰性序列】。
该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。
#要求只保留一个list中的奇数:
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]
# 要求删掉一个序列中的空字符串:
def not_empty(s):
return s and s.strip()
list(filter(not_empty, ['A', '', 'B', None, 'C', ' ']))
# 结果: ['A', 'B', 'C']
str.strip([chars]):移除字符串头尾指定的字符(默认为空格或换行符)或字符序列,返回移除字符串头尾指定的字符生成的新字符串。 chars -- 移除字符串头尾指定的字符序列(即要移除的部分)。
注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符。
问题:如何实现用filter()求素数。
sorted(iterable[,cmp[,key[,reverse]]]):对所有可迭代的对象进行排序操作。
- iterable -- 可迭代对象。
- cmp -- 比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。
- key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
- reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
我们再看一个字符串排序的例子:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']
默认情况下,对字符串排序,是按照ASCII的大小比较的,由于'Z' < 'a'
,结果,大写字母Z
会排在小写字母a
的前面。
给sorted
传入key函数,即可实现忽略大小写的排序:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']
sort 与 sorted 区别: | |
sort 是应用在 list 上的方法 | sorted 可以对所有可迭代的对象进行排序 |
list 的 sort 方法返回的是对已经存在的列表进行操作,无返回值 | 内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作 |
返回函数(返回值是一个函数)
问题:实现一个可变参数的求和,要求不需要立刻求和,不返回求和的结果,返回求和的函数,后续有需要再进行计算。
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
#调用lazy_sum()时,返回的结果是:
>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x101c6ed90>
#调用函数f时,才真正计算求和的结果:
>>> f()
25
本例中,在函数lazy_sum()中又定义了函数sum(),并且,内部函数sum()可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
注意:调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数(如f1 = lazy_sum(1,2,3,4) f2 = lazy_sum(1,2,3,4),f1≠f2),彼此的调用结果互不影响。
匿名函数
当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。
Python支持所谓的 匿名 或者 lambda 函数。匿名函数是一种通过单个语句生成函数的方式,其结果是返回值。匿名函数使用lambda关键字定义,该关键字仅表达“我们声明一个匿名函数”的意思。
语法:lambda arg1,arg2,.....argn:expression
匿名函数有个限制,就是只能有一个表达式,不用写return
,返回值就是该表达式的结果。
用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数。
>>> f = lambda x: x * x
>>> f
<function <lambda> at 0x101c6ef28>
>>> f(5)
25
#同样,也可以把匿名函数作为返回值返回,比如:
def build(x, y):
return lambda: x * x + y * y
装饰器(Decorator)
偏函数Partial function
Python的functools
模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。
即:当函数的参数个数太多,需要简化时,使用functools.partial
可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。
int()函数默认按十进制转换把字符串转换为整数,但它同时也提供修改base参数(默认值为10
),实现对N进制的转换。
【改变base值,就是指定N进制转换为十进制!】
>>> int('12345', base=8) #将八进制表示的12345转换为十进制表示的数
5349
>>> int('12345', 16) #将十六进制表示的12345转换为十进制表示的数
74565
如果要转换大量的二进制字符串,每次都传入int(x, base=2)
非常麻烦,我们就可以定义一个int2()函数来实现:
def int2(x,base =2):
return int(x,base)
functools.partial
就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2():
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85
简单总结functools.partial
的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
注意到上面的新的int2()函数,仅仅是把base
参数重新设定默认值为2
,但也可以在函数调用时传入其他值:
>>> int2('1000000', base=10)
1000000