文中知识点和代码示例学习自慕课网,python进阶部分(http://www.imooc.com/learn/317) 学习笔记
把函数作为参数
例1:求两个数字绝对值的和(abs(x)+abs(y)
)
def add(x,y,f):
return f(x) + f(y)
# add 函数的x,y,f 参数可以是任何值,如果 f 是个函数,则 x,y 两个参数分别带入到 f 函数中求值后,再做为 add 函数的参数传入。
print add(-5,9,abs)
例2:求√x + √y的值
import math
def add(x, y, f):
return f(x) + f(y)
print add(25, 9, math.sqrt)
map() 函数
它接收一个函数f
和一个list
,并通过把函数f
依次作用在list
的每个元素上,得到一个新的list
并返回。
例1:把列表总每个元素都取二次方
def f(x):
return x * x
print map(f,[1,2,3,4,5,6,7,8,9])
#[1, 4, 9, 16, 25, 36, 49, 64, 81]
例2:将列表中的字符串都变为首字母大写
def format_name(s):
return s.title()
print map(format_name, ['adam', 'LISA', 'barT'])
#['Adam', 'Lisa', 'Bart']
reduce() 函数
reduce()
函数接收的参数和map()
类似,一个函数f
,一个list
,但行为和map()
不同,reduce()
传入的函数f
必须接收两个参数,reduce()
对list
的每个元素反复调用函数f
,并返回最终结果值。
例1:求一个列表中所有元素的和
def f(x,y):
return x + y
print reduce(f,[1,2,3,4,5])
先计算头两个元素:f(1, 2),结果为3;
再把结果和第3个元素计算:f(3, 3),结果为6;
再把结果和第4个元素计算:f(6, 4),结果为10;
再把结果和第5个元素计算:f(10, 5),结果为15;
由于没有更多的元素了,计算结束,返回结果15.
#15
filter() 函数
filter()
函数接收一个函数f
和一个list
,这个函数f
的作用是对每个元素进行判断,返回True
或False
,filter()
根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list
。
例:删除一个list中的偶数,保留奇数
def is_odd(x):
return x % 2 == 1
print filter(is_odd(),[1,2,3,4,5,6,6,7,8,9])
#[1, 3, 5, 7, 9]
可以用来删除一个列表中我们不需要的元素。也可以使用如下方法来完成:
[i for i in [1,2,3,4,5,6,6,7,8,9] if i % 2 ==1 ]
#[1, 3, 5, 7, 9]
sorted() 函数
Python内置的 sorted()函数可对list进行排序:
>>>sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]
但 sorted()也是一个高阶函数,它可以接收一个比较函数来实现自定义排序,比较函数的定义是,传入两个待比较的元素 x, y,如果 x 应该排在 y 的前面,返回 -1,如果 x 应该排在 y 的后面,返回 1。如果 x 和 y 相等,返回 0。
例:实现倒序排列
def reversed_cmp(x, y):
if x > y:
return -1
if x < y:
return 1
return 0
print sorted([36, 5, 12, 9, 21], reversed_cmp)
#[36, 21, 12, 9, 5]
其他方法:
# 先排序,后反转
>>> sorted([36, 21, 12, 9, 5])[::-1]
[36, 21, 12, 9, 5]
# 使用reversed()方法,返回一个迭代器。 如果使用reverse(),是直接修改原列表,不会返回新的列表
>>> list(reversed(sorted([36, 5, 12, 9, 21])))
[36, 21, 12, 9, 5]
返回函数
Python的函数不但可以返回int
、str
、list
、dict
等数据类型,还可以返回函数!
例1:定义一个函数 f(),我们让它返回一个函数 g
def f():
print 'call f()...'
# 定义函数g:
def g():
print 'call g()...'
# 返回函数g:
return g
>>> x = f() # 调用f()
call f()...
>>> x # 变量x是f()返回的函数g:
>>> x() # x指向函数g,因此可以调用
call g()... # 调用x()就是执行g()函数定义的代码
例2:写一个函数calc_prod(lst)
,它接收一个list
,返回一个函数,返回函数可以计算参数的乘积。
def calc_prod(lst):
def f(x,y):
return x * y
def chengji():
return reduce(f,lst)
return chengji
f = calc_prod([1, 2, 3, 4])
print f()
闭包
例如上一小节返回函数中的例2,chengji
内层函数引用了外层函数calc_prod
的lst
变量。像这种内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)。
闭包的特点:返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。举例如下:
# 希望一次返回3个函数,分别计算1x1,2x2,3x3:
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
#9 9 9
原因就是当count()
函数返回了3个函数时,这3个函数所引用的变量i
的值已经变成了3。由于f1、f2、f3并没有被调用,所以,此时他们并未计算i*i
,当 f1 被调用时:
>>> f1()
9 # 因为f1现在才计算i*i,但现在i的值已经变为3
因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量。因此改成如下代码:
# 法一
def count():
fs = []
for i in range(1, 4):
def f(j):
# 借助函数f来避免引用循环变量i
def g():
return j*j
return g
r = f(i)
fs.append(r)
return fs
f1, f2, f3 = count()
print f1(), f2(), f3()
#1 4 9
# 法二
def count():
fs = []
def f(j):
def g():
return j*j
return g
for i in range(1, 4):
r = f(i)
fs.append(r)
return fs
f1, f2, f3 = count()
print f1(), f2(), f3()
#1 4 9
匿名函数 lambda
高阶函数可以接收函数做参数,有些时候,我们不需要显式地定义函数,直接传入匿名函数更方便。
在Python
中,对匿名函数提供了有限支持。还是以map()
函数为例,计算f(x)=x*x
时,除了定义一个f(x)
的函数外,还可以直接传入匿名函数
>>> a = lambda x:x*x
>>> a(5)
25
>>> map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]
匿名函数有个限制,就是只能有一个表达式,不写return
,返回值就是该表达式的结果。
def is_not_empty(s):
return s and len(s.strip()) > 0
filter(is_not_empty, ['test', None, '', 'str', ' ', 'END'])
返回函数的时候,也可以返回匿名函数:
# 使用 if..else 来实现abs函数的功能
>>> myabs = lambda x: -x if x < 0 else x
>>> myabs(-1)
1
>>> myabs(1)
1
例:使用匿名函数简化代码
# 源代码
def is_not_empty(s):
return s and len(s.strip()) > 0
filter(is_not_empty, ['test', None, '', 'str', ' ', 'END'])
# 使用匿名函数
print filter(lambda x:x and len(x.strip()) > 0,['test', None, '', 'str', ' ', 'END'])
# 使用 for..in..if 语句
print [i for i in ['test', None, '', 'str', ' ', 'END'] if i and len(i.strip())>0]
偏函数
当一个函数有很多参数时,调用者就需要提供多个参数。如果减少参数个数,就可以简化调用者的负担。
比如,int()
函数可以把字符串转换为整数,当仅传入字符串时,int()
函数默认按十进制转换:
>>> int('12345')
12345
但int()
函数还提供额外的base
参数,默认值为10。如果传入base
参数,就可以做 N 进制的转换:
>>> int('12345', base=8)
5349
>>> int('12345', 16)
74565
假设要转换大量的二进制字符串,每次都传入int(x, base=2)
非常麻烦,于是,我们想到,可以定义一个int2()
的函数,默认把base=2
传进去:
def int2(x, base=2):
return int(x, base)
# 这样,我们转换二进制就非常方便了:
>>> int2('1000000')
64
>>> int2('1010101')
85
functools.partial
就是帮助我们创建一个偏函数的,不需要我们自己定义int2()
,可以直接使用下面的代码创建一个新的函数int2
:
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85
所以,functools.partial
可以把一个参数多的函数变成一个参数少的新函数,少的参数需要在创建时指定默认值,这样,新函数调用的难度就降低了。