本节摘要:高阶函数的基本介绍,map( )函数、reduce( )函数、filter( )函数、sorted( )函数的介绍
Higher-order function,即高阶函数。
例如,Python内置的求绝对值的函数abs():
>>> abs(-5) # 调用该函数用此代码,abs(-5)是函数调用
5
>>> abs # 只写abs,abs是函数本身
<built-in function abs>
>>> x = abs(-5) # 要获得函数调用结果,可以把结果赋值给变量,x变量指向函数abs()
>>> x
5
>>> x = abs # 把函数本身赋值给变量
>>> x
<built-in function abs>
结论:函数本身也可以赋值给变量,即:变量可以指向函数。
如果一个变量指向了一个函数,则可通过调用该变量来调用这个函数:
>>> f = abs # 变量f现在已经指向了abs函数本身
>>> f(-8) # 直接调用abs()函数和调用变量f()完全相同
8
函数名也是变量,函数名其实就是指向函数的变量,对于abs()
这个函数,完全可以把函数名abs
看成变量,它指向一个可以计算绝对值的函数
如果把abs
指向其他对象
>>> abs = 10
>>> abs
10
>>> abs(-10)
Traceback (most recent call last):
File "" , line 1, in <module>
TypeError: 'int' object is not callable
把abs
指向10
后,就无法通过abs(-10)
调用该函数了!因为abs
这个变量已经不指向求绝对值函数而是指向一个整数10
要恢复abs
函数,请exit()
退出后重启Python交互环境。
注:由于
abs
函数实际上是定义在import builtins
模块中的,所以要让修改abs
变量的指向在其它模块也生效,要用import builtins; builtins.abs = 10
。
既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,函数就可以传入函数和返回函数,这种函数就叫高阶函数。
简单高阶函数例如:
def add(x, y, f):
return f(x) + f(y)
当调用add(2, -5, abs)时,参数x,y和f分别接收2,-5和abs,根据函数定义,可以推导计算过程为:
x = 2
y = -5
f = abs
f(x) + f(y) ==> abs(2) + abs(-5) ==> 7
return 7
代码验证:
>>> def add(x, y, f):
... return f(x) + f(y)
...
>>> print add(2, -5, abs)
7
编写高阶函数,就是让函数的参数能够接收别的函数。
map( ) 是一种高阶函数,它接收两个参数,第一个是函数,第二个是Iterable,即可迭代对象。map( )将函数依次作用于Iterable的每个元素,并将结果以Iterator,即迭代器的形式返回。这种方式类似构造出了数学中的映射过程。
例如,函数 f ( x ) = x 2 f(x)=x^2 f(x)=x2,把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上
>>> 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输出。
该例子也可用循环得出:
>>> L = []
>>> for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]:
... L.append(f(n))
...
>>> print L
[1, 4, 9, 16, 25, 36, 49, 64, 81]
map()作为高阶函数,还可以计算任意复杂的函数
例如,把list所有数字转为字符串:
>>> list(map(str, [1, 2, 3, 4, 5])) #str()可以将整数转化为字符串
['1', '2', '3', '4', '5']
我们还能用列表生成器得到同样的结果,但要用到for语句:
>>> b = [str(x) for x in [1, 2, 3, 4, 5]]
>>> b
['1', '2', '3', '4', '5']
reduce( )函数同样接收一个函数和一个Iterable两个参数,不同的是所传入的函数必须要接收两个参数,而reduce是把函数作用的结果继续与序列中下一个元素做累积计算。
其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
例如,序列求和一般用用Python内建函数sum()
,也可以用reduce
实现:
>>> from functools import reduce
>>> def add(x, y):
... return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25
reduce可用于把序列转化为相应的整数显示,例如整数13579
>>> from functools import reduce #加入内置函数reduce
>>> def fn(x, y):
... return x * 10 + y
...
>>> reduce(fn, [1, 2, 3, 4, 5])
12345
reduce配合map(),可以写出把str转换为int的函数:
>>> def fn(x, y):
... return x * 10 + y
...
>>> def char2num(s):
... return {
'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
...
>>> reduce(fn, map(char2num, '13579')) #map(char2num, ['1', '3', '5', '7', '9'])
13579
>>> 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
的函数就是:
def str2int(s):
def fn(x, y):
return x * 10 + y
def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
return reduce(fn, map(char2num, s))
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函数进一步简化成:
def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
def str2int(s):
return reduce(lambda x,y: x*10+y, map(char2num, s))
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))
lambda函数的用法在后面介绍
利用map()
函数,把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:[‘adam’, ‘LISA’, ‘barT’],输出:[‘Adam’, ‘Lisa’, ‘Bart’]:
# -*- coding: utf-8 -*-
def normalize(name):
pass
# 测试:
L1 = ['adam', 'LISA', 'barT']
L2 = list(map(normalize, L1))
print(L2)
【交作业】
# -*- coding: utf-8 -*-
def normalize(name):
return name[0].upper() + name[1:].
L1 = ['adam', 'LISA', 'barT']
L2 = list(map(normalize, L1))
print L2
输出结果:
['Adam', 'Lisa', 'Bart']
Python提供的sum()函数可以接受一个list并求和,请编写一个prod()函数,可以接受一个list并利用reduce()求积:
# -*- coding: utf-8 -*-
from functools import reduce
def prod(L):
pass
print('3 * 5 * 7 * 9 =', prod([3, 5, 7, 9]))
if prod([3, 5, 7, 9]) == 945:
print('测试成功!')
else:
print('测试失败!')
【交作业】
# -*- coding: utf-8 -*-
from functools import reduce
def prod(L):
def mul(x, y):
return x * y
return reduce(mul, L)
print '3 * 5 * 7 * 9 =', prod([3, 5, 7, 9])
if prod([3, 5, 7, 9]) == 945:
print('测试成功!')
else:
print('测试失败!')
输出结果:
3 * 5 * 7 * 9 = 945
测试成功!
利用map和reduce编写一个str2float函数,把字符串’123.456’转换成浮点数123.456:
# -*- coding: utf-8 -*-
from functools import reduce
def str2float(s):
print('str2float(\'123.456\') =', str2float('123.456'))
if abs(str2float('123.456') - 123.456) < 0.00001:
print('测试成功!')
else:
print('测试失败!')
【交作业】
# -*- coding: utf-8 -*-
from functools import reduce
def str2float(s):
DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
inde = s.find('.')
leng = len(s)
s1 = s[:inde]+ s[inde+1 :]
return reduce(lambda x, y: x * 10 + y, map(lambda x : DIGITS[x], s1))/(10**(leng-inde-1))
filter,即过滤,filter( )函数的作用是过滤序列。与上面map() reduce()两个函数类似,接收一个函数与一个序列,与之不同的是,filter()在把传入的函数此作用于序列的每个元素之后,根据返回值True或False来保留或移除该元素。用filter()这个高阶函数,关键在于正确实现一个“筛选”函数
例如,如写一个list中,删掉偶数,只保留奇数的函数:
>>> def is_odd(n):
... return n % 2 == 1
...
>>> list(filter(is_odd, [1, 2, 3, 4, 5, 6, 7]))
[1, 3, 5, 7]
例二,把一个序列中的空字符串删掉:
>>> def not_empty(s):
... return s and s.strip() # strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列
...
>>> list(filter(not_empty, ['A', '', 'B', None, 'C', ' ']))
['A', 'B', 'C']
注意:filter( )函数返回的也是Iterator,惰性序列,需要用list( )来转化才能直接显示计算结果。
计算素数的一个方法是埃氏筛法,它的算法理解起来非常简单:
首先,列出从2开始的所有自然数,构造一个序列:
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
取序列的第一个数2,它一定是素数,然后用2把序列的2的倍数筛掉:
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
取新序列的第一个数3,它一定是素数,然后用3把序列的3的倍数筛掉:
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
取新序列的第一个数5,然后用5把序列的5的倍数筛掉:
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
不断筛下去,就可以得到所有的素数。
用Python来实现这个算法:
def _odd_iter(): #定义一个初始值为3的奇数初始序列生成器
n = 1
while True:
n = n + 2
yield n
def _not_divesible(n): #定义筛选函数,不是n的倍数则返回True,筛选出不是n的倍数的数
return lambda x : x % n > 0 #返回lambda函数。%在python中为求模运算。此表达式表示 x被n除后的余数大于0
def primes(): #定义一个初始值为2,
yield 2
it = _odd_iter() #it为序列n的生成器,初始序列
while True:
n = next(it) #返回序列n的第一个值
yield n
it = filter(_not_divesible(n), it) #过滤序列中为n的倍数的值
这个生成器先返回第一个素数2,然后,利用filter()不断产生筛选后的新的序列。
由于primes()也是一个无限序列,所以调用时需要设置一个退出循环的条件:
# 打印1000以内的素数:
for n in primes(): #for循环取值
if n < 100: #打印100以为的素数
print(n)
else:
break
注意到Iterator是惰性计算的序列,所以我们可以用Python表示“全体自然数”,“全体素数”这样的序列,而代码非常简洁。
总结:
filter()的作用是从一个序列中筛出符合条件的元素。由于filter()使用了惰性计算,所以只有在取filter()结果的时候,才会真正筛选并每次返回下一个筛出的元素。
回数是指从左向右读和从右向左读都是一样的数,例如12321,909。请利用filter()筛选出回数:
# -*- coding: utf-8 -*-
def is_palindrome(n):
pass
【交作业】
# -*- coding: utf-8 -*-
def is_palindrome(n):
if str(n)[0:1] == str(n)[-1:]: # 美中不足只对3位数字有效,故看答案2
return n
# 测试:
output = filter(is_palindrome, range(1, 1000))
print('1~1000:', list(output))
if list(filter(is_palindrome, range(1, 200))) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191]:
print('测试成功!')
else:
print('测试失败!')
输入结果:
('1~1000:', [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191, 202, 212, 222, 232, 242, 252, 262, 272, 282, 292, 303, 313, 323, 333, 343, 353, 363, 373, 383, 393, 404, 414, 424, 434, 444, 454, 464, 474, 484, 494, 505, 515, 525, 535, 545, 555, 565, 575, 585, 595, 606, 616, 626, 636, 646, 656, 666, 676, 686, 696, 707, 717, 727, 737, 747, 757, 767, 777, 787, 797, 808, 818, 828, 838, 848, 858, 868, 878, 888, 898, 909, 919, 929, 939, 949, 959, 969, 979, 989, 999])
测试成功!
def is_palindrome(n):
return str(n) == str(n)[::-1] # 将字符串逆序
def is_palindrome(n):
if str(n)[:]==str(n)[::-1]:
return int(n) # 其实n的形式并没有变化,int(n)多此一举
def is_palindrome(n):
return str(n)[::1]==str(n)[::-1] # 多此一举的[::1]正序hhh
sort,分类、排序的意思。在Python中sorted( )函数可以将list排序。
>>> sorted([21, 5, -3, -10, 18])
[-10, -3, 5, 18, 21]
sort( )函数还能接收一个key函数来自定义排序,也就是先把传入的函数作用于list,根据结果进行排序,把传入的初始序列按照结果的顺序进行排序。
例如按绝对值大小排序:
>>> sorted([21, 5, -3, -10, 18], key=abs )
[-3, 5, -10, 18, 21]
key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。对比原始的list和经过key=abs处理过的list:
list = [21, 5, -3, -10, 18]
keys = [21, 5, 3, 10, 18]
然后sorted()函数按照keys进行排序,并按照对应关系返回list相应的元素:
keys排序结果 => [3, 5, 10, 18, 21]
| | | | |
最终结果 => [-3, 5, -10, 18, -21]
>>> sorted([21, 5, -3, -10, 18], key=abs )
[-3, 5, -10, 18, 21]
例二,倒序排序:
先自定义一个reversed_cmp函数:
def reversed_cmp(x, y):
if x > y:
return -1
if x < y:
return 1
return 0
传入自定义的比较函数reversed_cmp,就可以实现倒序排序:
>>> sorted([36, 5, 12, 9, 21], reversed_cmp)
[36, 21, 12, 9, 5]
例三:字符串排序
>>> sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']
默认情况下,对字符串排序,是按照ASCII的大小比较的,由于’Z’ < ‘a’,结果,大写字母Z会排在小写字母a的前面。
若要忽略大小写,按照字母序排序。要实现这个算法,不必对现有代码大加改动,只要我们能用一个key函数把字符串映射为忽略大小写排序即可。忽略大小写来比较两个字符串,实际上就是先把字符串都变成大写(或者都变成小写),再比较。
给sorted传入key函数,即可实现忽略大小写的排序:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']
要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True
:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']
由此可知,高阶函数的抽象能力是非常强大的,核心代码可以保持得非常简洁。
sorted()也是一个高阶函数。用sorted()排序的关键在于实现一个映射函数。
假设我们用一组tuple表示学生名字和成绩:
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
请用sorted()
对上述列表分别按名字排序:
# -*- coding: utf-8 -*-
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
def by_name(t):
pass
L2 = sorted(L, key=by_name)
print(L2)
再按成绩从高到低排序:
# -*- coding: utf-8 -*-
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
def by_score(t):
pass
L2 = sorted(L, key=by_score)
print(L2)
【交作业】
按名字排序:
# -*- coding: utf-8 -*-
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
def by_name(t):
return t[0]
L2 = sorted(L, key=by_name)
print(L2)
输出结果:
[('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]
按成绩从高到低排序:
# -*- coding: utf-8 -*-
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
def by_score(t):
return -t[1]
L2 = sorted(L, key=by_score)
print(L2)
输出结果:
[('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]
sorted(L, key=lambda x: str.lower(x[0]))
# -*- coding: utf-8 -*-
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
def by_name(t):
return t[0].lower()
def by_score(t):
return -t[1].lower()
1.常用的函数。abs( )取绝对值,max( )取最大值,int( )把其他数据类型转化为整数,hex( )把整数转化为十六进制。
2.在Python交互环境中定义函数时,注意Python会出现…的提示。函数定义结束后需要按两次回车才能重新回到>>>提示符下。
3.pass语句可以用来作为占位符,如果有一定要写但没想好怎么写的内容时,可以先写pass,确保其他语句正常运行。比如:
num = 0
if num < 100:
pass
这样语句并不会出错。
补充几个函数的用法:
‘字符串’.lower( )
函数可以把字符串变成小写。
s.index(‘x’)
返回字符串中字符’x’的索引值;
s.strip( )
函数去除字符串首尾的空格或其他指定字符;
s[::-1]
把字符串s从尾到头顺序重新排列。
/) /)
ฅ(• - •)ฅ ~ ~ ฅ’ω’ฅ