python高阶函数学习笔记-廖雪峰课程笔记

#python进阶学习笔记1(廖雪峰课程)

##高阶函数
python和Js很像,支持将函数作为变量输入到参数的参数里面去,此时将函数作为参数的函数称之为高阶函数
变量也是可以指向函数的,例如:

1
2
3
fun=abs()
fun(-10)
#输出的结果就是 abs(-10)=10的结果

一般存在一下几个常用的高阶函数,将list和function作为参数输入处理:

map函数的例子

1
2
3
4
5
def f(x):
return x*x

print map(f,[range(1,10)])
#输出的结果是[x for x in range(1,82) if math.sqrt(x) in range(1,10)]

reduce函数

1
2
3
4
def f(x,y):
return x+y

reduce(f,range(1,10)[::2])

该函数输出的结果是:

1
2
3
4
5
先计算头两个元素:f(1, 3),结果为4;
再把结果和第3个元素计算:f(4, 5),结果为9;
再把结果和第4个元素计算:f(9, 7),结果为16;
再把结果和第5个元素计算:f(16, 9),结果为25;
由于没有更多的元素了,计算结束,返回结果25。

reduce函数还可以指定计算的初始值:

1
2
reduce(f,range(1,10)[::2],100)
#输出的结果:125

filter()函数:filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。

1
2
3
4
5
def is_odd(x):
return x %2 ==1

filter(is_odd,[1,4,6,7,9,12,17])
#输出结果 [1,7,9,17]

利用filter(),可以完成很多有用的功能,例如,删除 None 或者空字符串:

1
2
3
4
5
6
7
8
9
10
11
12
def is_not_empty(s):
return s and len(s.strip())>0

filter(is_not_empty, ['test', None, '', 'str', ' ', 'END'])
#输出结果 :['test', 'str', 'END']

#注意: s.strip(rm) 删除 s 字符串中开头、结尾处的 rm 序列的字符。
#当rm为空时,默认删除空白符(包括'\n', '\r', '\t', ' '),如下:

a = ' 123'
a.strip()
#结果是 '123'

自定义排序函数

sorted()也是一个高阶函数,它可以接收一个比较函数来实现自定义排序,比较函数的定义是,传入两个待比较的元素 x, y,如果 x 应该排在 y 的前面,返回 -1,如果 x 应该排在 y 的后面,返回 1。如果 x 和 y 相等,返回 0。

因此,如果我们要实现倒序排序,只需要编写一个reversed_cmp函数:

1
2
3
4
5
6
7
8
9
10
def reversed_cmp(x, y):
if x > y:
return -1
if x < y:
return 1
return 0

# 实现倒序排序
sorted([36, 5, 12, 9, 21], reversed_cmp)
#输出结果:[36, 21, 12, 9, 5]

python中的函数作为变量看待

python最骚的就是和JS一样将函数作为变量看待,一个函数的返回值是可以返回一个函数!!!,因为python将函数作为变量看待
Python的函数不但可以返回int、str、list、dict等数据类型,还可以返回函数!
例如,定义一个函数 f(),我们让它返回一个函数 g,可以这样写:

1
2
3
4
5
6
7
8
def f():
print 'call f()'
#定义一个g函数
def g():
print 'call g()'

#函数g作为返回,注意返回的是函数名!!!
return g

执行的结果:

1
2
3
4
5
6
>>> x = f()   # 调用f()
call f()...
>>> x # 变量x是f()返回的函数:
<function g at 0x1037bf320>
>>> x() # x指向函数,因此可以调用
call g()... # 调用x()就是执行g()函数定义的代码

注意返回函数的时候是返回函数名,不是调用函数

1
2
3
4
5
6
7
def myabs():
return abs
#返回的是函数名

def myabs1(x):
return abs(x)
#返回的是值!!!

python中的闭包操作

和Js一模一样,在一个函数里面定义另一个函数,在这个函数里面存在定义函数的外部变量封装起来

1
2
3
4
5
def f():
a='local'
def g():
print a
return g

这里看起来函数g中的变量a是外界看不到的变量,廖雪峰老师的进阶课程中对闭包的解释:
例子:

1
2
3
4
def calc_sum(lst):
def lazy_sum():
return sum(lst)
return lazy_sum

注意: 发现没法把 lazy_sum 移到 calc_sum 的外部,因为它引用了 calc_sum 的参数 lst。

像这种内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)。

闭包的特点是返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。
一个闭包错误的例子:

1
2
3
4
5
6
7
8
9
10
	# 希望一次返回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()

你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果全部都是 9(请自己动手验证)。

原因就是当count()函数返回了3个函数时,这3个函数所引用的变量 i 的值已经变成了3。由于f1、f2、f3并没有被调用,所以,此时他们并未计算 i*i,当 f1 被调用时:

1
2
>>> f1()
9 # 因为f1现在才计算i*i,但现在i的值已经变为3

因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量。
我的理解
python在执行对应的程序的时候才去读取对应的i值,此时的for已经结束,因此i一直都是3,修改代码:

1
2
3
4
5
6
7
8
9
10
11
def count():
fs=[]
for i in range(1,4):
def f(x=i):
return x*x
fs.append(f)
return fs

f1,f2,f3=count()
print f1(),f2(),f3()
#输出理想的结果 1,4,9

Python中的闭包其实还有一个问题,外层函数的变量和里层函数的变量重名,但是这个变量不是总的函数的外部变量,这就会出bug:

1
2
3
4
5
6
7
8
9
10
def fun1():
count=0
def func():
count+=1
print 'Now count is',count
return func

test=fun1()
test()
#直接会报错哦

解决方案加入关键词 nonlocal(python 3支持)

1
2
3
4
5
6
7
8
9
10
11
def fun1():
count=0
def func():
nonlocal count+=1
print 'Now count is',count
return func

test=fun1()
test()
test()
test()

python中的匿名函数

套路和JS很像直接在参数传入的过程中定义一个简单的函数
但是JS是有关键字function的,而python中的关键词是lambda!!!
简单示例函数:

1
2
map(lambda x:x*x,range(1,10))
#输出结果[1,4,9,16,25,36,49,64,84]

匿名函数的实体定义类似:

1
2
def f(x):
return x*x

关键字lambda 表示匿名函数,冒号前面的 x 表示函数参数。

匿名函数有个限制,就是只能有一个表达式,不写return,返回值就是该表达式的结果。
这也是python比JS的不足,JS的匿名函数没有那么多限制就是直接定义,但是python的灵活就在于函数对象的使用,例如:

1
2
sorted([1, 3, 9, 5, 0], lambda x,y: -cmp(x,y))
#结果:[9, 5, 3, 1, 0]

返回函数的时候,也可以返回匿名函数:

1
2
3
4
5
>>> myabs = lambda x: -x if x < 0 else x 
>>> myabs(-1)
1
>>> myabs(1)
1

热评文章